using System; using System.Threading; using System.Threading.Tasks; using MessagePack; using Serilog; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Backend; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Shared; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Shared.Contracts; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Ipc; /// /// Real FOCAS frame handler. Deserializes each request DTO, delegates to /// , re-serializes the response. The backend owns the /// Fwlib32 handle + STA thread — the handler is pure dispatch. /// public sealed class FwlibFrameHandler : IFrameHandler { private readonly IFocasBackend _backend; private readonly ILogger _logger; public FwlibFrameHandler(IFocasBackend backend, ILogger logger) { _backend = backend ?? throw new ArgumentNullException(nameof(backend)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task HandleAsync(FocasMessageKind kind, byte[] body, FrameWriter writer, CancellationToken ct) { try { switch (kind) { case FocasMessageKind.Heartbeat: { var hb = MessagePackSerializer.Deserialize(body); await writer.WriteAsync(FocasMessageKind.HeartbeatAck, new HeartbeatAck { MonotonicTicks = hb.MonotonicTicks, HostUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), }, ct).ConfigureAwait(false); return; } case FocasMessageKind.OpenSessionRequest: { var req = MessagePackSerializer.Deserialize(body); var resp = await _backend.OpenSessionAsync(req, ct).ConfigureAwait(false); await writer.WriteAsync(FocasMessageKind.OpenSessionResponse, resp, ct).ConfigureAwait(false); return; } case FocasMessageKind.CloseSessionRequest: { var req = MessagePackSerializer.Deserialize(body); await _backend.CloseSessionAsync(req, ct).ConfigureAwait(false); return; } case FocasMessageKind.ReadRequest: { var req = MessagePackSerializer.Deserialize(body); var resp = await _backend.ReadAsync(req, ct).ConfigureAwait(false); await writer.WriteAsync(FocasMessageKind.ReadResponse, resp, ct).ConfigureAwait(false); return; } case FocasMessageKind.WriteRequest: { var req = MessagePackSerializer.Deserialize(body); var resp = await _backend.WriteAsync(req, ct).ConfigureAwait(false); await writer.WriteAsync(FocasMessageKind.WriteResponse, resp, ct).ConfigureAwait(false); return; } case FocasMessageKind.PmcBitWriteRequest: { var req = MessagePackSerializer.Deserialize(body); var resp = await _backend.PmcBitWriteAsync(req, ct).ConfigureAwait(false); await writer.WriteAsync(FocasMessageKind.PmcBitWriteResponse, resp, ct).ConfigureAwait(false); return; } case FocasMessageKind.ProbeRequest: { var req = MessagePackSerializer.Deserialize(body); var resp = await _backend.ProbeAsync(req, ct).ConfigureAwait(false); await writer.WriteAsync(FocasMessageKind.ProbeResponse, resp, ct).ConfigureAwait(false); return; } default: await writer.WriteAsync(FocasMessageKind.ErrorResponse, new ErrorResponse { Code = "unknown-kind", Message = $"Kind {kind} is not handled by the Host" }, ct).ConfigureAwait(false); return; } } catch (OperationCanceledException) { throw; } catch (Exception ex) { _logger.Error(ex, "FwlibFrameHandler error processing {Kind}", kind); await writer.WriteAsync(FocasMessageKind.ErrorResponse, new ErrorResponse { Code = "backend-exception", Message = ex.Message }, ct).ConfigureAwait(false); } } public IDisposable AttachConnection(FrameWriter writer) => IFrameHandler.NoopAttachment.Instance; }