From b544d5cccd76735e034c8055918a50927b437474 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期二, 10 十一月 2026 08:29:04 +0800
Subject: [PATCH] fix: 维护模式重复进入失败与按钮灰死 + Broker 并发验证用例
---
Aging.ResourceContracts/BrokerIpc.cs | 91 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/Aging.ResourceContracts/BrokerIpc.cs b/Aging.ResourceContracts/BrokerIpc.cs
new file mode 100644
index 0000000..1aef23f
--- /dev/null
+++ b/Aging.ResourceContracts/BrokerIpc.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Buffers.Binary;
+using System.Text;
+using System.Text.Json;
+
+namespace Aging.ResourceContracts
+{
+ /// <summary>
+ /// Broker IPC 甯у崗璁細4 瀛楄妭澶х闀垮害鍓嶇紑 + UTF8 JSON 灏佸銆�
+ /// 鍗忚涓荤増鏈笉鍖归厤鏃� Broker 鐩存帴鎷掔粷杩炴帴锛圴3.0 搂13锛夈��
+ /// DTO 绾挎牸寮忕嫭绔嬩簬 LeaseGrant 绛夊彧璇绘瀯閫犲櫒绫诲瀷锛岃閬� System.Text.Json 鍙嶅簭鍒楀寲闄愬埗銆�
+ /// </summary>
+ public static class BrokerIpc
+ {
+ public const int ProtocolVersion = 1;
+
+ /// <summary>鍗曞抚鏈�澶у瓧鑺傛暟锛�1MB锛夛紝瓒呴檺瑙嗕负鍗忚閿欒</summary>
+ public const int MaxFrameBytes = 1024 * 1024;
+
+ private static readonly JsonSerializerOptions JsonOpts = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true,
+ DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
+ };
+
+ // ==================== 甯х紪瑙g爜 ====================
+
+ /// <summary>缂栫爜涓轰竴甯э細[4 瀛楄妭澶х闀垮害][UTF8 JSON]</summary>
+ 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;
+ }
+
+ /// <summary>浠� payload JSON 瑙g爜灏佸銆傞潪娉� JSON 鎶� FormatException銆�</summary>
+ public static IpcEnvelope DecodeEnvelope(string payloadJson)
+ {
+ var env = JsonSerializer.Deserialize<IpcEnvelope>(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 ====================
+
+ /// <summary>SubmitTask Body锛氫竴娆℃湁鐣岃澶囧姩浣滐紙OneShot 鍚屾妯″瀷锛�</summary>
+ public sealed class SubmitBody
+ {
+ public string DeviceId { get; set; } = "";
+ /// <summary>鐧藉悕鍗曟搷浣滅爜锛欳ONNECT/DISCONNECT/SEND/QUERY/STATUS</summary>
+ public string OperationCode { get; set; } = "";
+ /// <summary>鎿嶄綔杞借嵎锛堝懡浠ゆ垨鏌ヨ瀛楃涓诧級</summary>
+ public string Payload { get; set; } = "";
+ /// <summary>浠茶璧勬簮閿紙broker 渚� fence 鍐嶆牎楠岀敤锛屽 CAN-003:Generic锛�</summary>
+ public string ResourceKey { get; set; } = "";
+ /// <summary>瀹㈡埛绔繘绋嬪疄渚嬭韩浠� boot+pid锛屼笌 arb.db owner 瀵归綈</summary>
+ public string ClientInstanceId { get; set; } = "";
+ }
+
+ /// <summary>SubmitTask 鍝嶅簲 Body</summary>
+ 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<T>(string json) where T : class
+ {
+ if (string.IsNullOrWhiteSpace(json)) return default;
+ try { return JsonSerializer.Deserialize<T>(json, JsonOpts); }
+ catch (JsonException) { return default; }
+ }
+
+ /// <summary>deadline 璁$畻锛歎TC 姣</summary>
+ public static long NowUtcMs()
+ => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ }
+}
--
Gitblit v1.9.1