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();
}
}