using System; using System.Buffers.Binary; using System.Text; using System.Text.Json; namespace Aging.ResourceContracts { /// /// Broker IPC 帧协议:4 字节大端长度前缀 + UTF8 JSON 封套。 /// 协议主版本不匹配时 Broker 直接拒绝连接(V3.0 §13)。 /// DTO 线格式独立于 LeaseGrant 等只读构造器类型,规避 System.Text.Json 反序列化限制。 /// public static class BrokerIpc { public const int ProtocolVersion = 1; /// 单帧最大字节数(1MB),超限视为协议错误 public const int MaxFrameBytes = 1024 * 1024; private static readonly JsonSerializerOptions JsonOpts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; // ==================== 帧编解码 ==================== /// 编码为一帧:[4 字节大端长度][UTF8 JSON] public static byte[] EncodeFrame(string json) { var payload = Encoding.UTF8.GetBytes(json); if (payload.Length > MaxFrameBytes) throw new InvalidOperationException($"帧超限: {payload.Length} > {MaxFrameBytes}"); var frame = new byte[4 + payload.Length]; BinaryPrimitives.WriteInt32BigEndian(frame.AsSpan(), payload.Length); payload.CopyTo(frame, 4); return frame; } /// 从 payload JSON 解码封套。非法 JSON 抛 FormatException。 public static IpcEnvelope DecodeEnvelope(string payloadJson) { var env = JsonSerializer.Deserialize(payloadJson, JsonOpts); if (env == null || string.IsNullOrEmpty(env.MessageType)) throw new FormatException("无效 IPC 封套"); return env; } public static string EncodeEnvelope(IpcEnvelope env) => JsonSerializer.Serialize(env, JsonOpts); // ==================== 线格式 DTO ==================== /// SubmitTask Body:一次有界设备动作(OneShot 同步模型) public sealed class SubmitBody { public string DeviceId { get; set; } = ""; /// 白名单操作码:CONNECT/DISCONNECT/SEND/QUERY/STATUS public string OperationCode { get; set; } = ""; /// 操作载荷(命令或查询字符串) public string Payload { get; set; } = ""; /// 仲裁资源键(broker 侧 fence 再校验用,如 CAN-003:Generic) public string ResourceKey { get; set; } = ""; /// 客户端进程实例身份 boot+pid,与 arb.db owner 对齐 public string ClientInstanceId { get; set; } = ""; } /// SubmitTask 响应 Body public sealed class ResultBody { public string State { get; set; } = ""; // Succeeded/Failed/RejectedStaleLease/RejectedConfigMismatch/... public string ErrorCode { get; set; } = ""; public string Readback { get; set; } = ""; public long ServerEpoch { get; set; } } public static string SerializeBody(object body) => JsonSerializer.Serialize(body, JsonOpts); public static T? DeserializeBody(string json) where T : class { if (string.IsNullOrWhiteSpace(json)) return default; try { return JsonSerializer.Deserialize(json, JsonOpts); } catch (JsonException) { return default; } } /// deadline 计算:UTC 毫秒 public static long NowUtcMs() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } }