在 AiVideo 中能看到图像

增加了在线状态同步逻辑
This commit is contained in:
2026-01-09 12:30:36 +08:00
parent 3d47c8f009
commit 3351ae739e
31 changed files with 1090 additions and 477 deletions

View File

@@ -1,16 +1,27 @@
namespace SHH.Contracts
using MessagePack;
namespace SHH.Contracts
{
/// <summary>
/// 通用指令执行结果 (Response)
/// </summary>
[MessagePackObject]
public class CommandResult
{
#region --- 0. ---
[Key(0)]
public string Protocol { get; set; } = "COMMAND_RESULT";
#endregion
#region --- ---
/// <summary>
/// 回执 ID (必须与请求包的 RequestId 一致)
/// <para>客户端靠这个 ID 来找到对应的 await Task</para>
/// </summary>
[Key(1)]
public string RequestId { get; set; }
#endregion
@@ -20,17 +31,20 @@
/// <summary>
/// 执行是否成功
/// </summary>
[Key(2)]
public bool Success { get; set; }
/// <summary>
/// 结果消息 (成功提示或错误原因)
/// </summary>
[Key(3)]
public string Message { get; set; }
/// <summary>
/// 返回的数据 (JSON 或 Base64 字符串)
/// <para>示例: 截图的 Base64或者查询到的设备列表 JSON</para>
/// </summary>
[Key(4)]
public string Data { get; set; }
#endregion
@@ -42,10 +56,17 @@
/// <para>从客户端发出指令,到收到服务端回执的总时长</para>
/// <para>注意:该字段由客户端收到回执后自动计算填充,服务端不需要赋值</para>
/// </summary>
[Key(5)]
public double ElapsedMilliseconds { get; set; }
#endregion
/// <summary>
/// 时间戳
/// </summary>
[Key(6)]
public long Timestamp { get; set;}
#region --- ---
/// <summary>

View File

@@ -1,4 +1,5 @@
using System;
using MessagePack;
using System;
namespace SHH.Contracts
{
@@ -6,26 +7,41 @@ namespace SHH.Contracts
/// 通用指令请求载体 (Request)
/// <para>用于 NetMQ 的 Request-Reply 或 Router-Dealer 模式</para>
/// </summary>
[MessagePackObject]
public class CommandPayload
{
#region --- 0. ---
/// <summary>
/// 协议类型标识
/// <para>建议值: "COMMAND" 或 "指令包"</para>
/// </summary>
[Key(0)]
public string Protocol { get; set; } = "COMMAND";
#endregion
#region --- ---
/// <summary>
/// 指令代码 (路由键)
/// <para>示例: "PTZ", "RECORD_START", "SERVER_REGISTER"</para>
/// </summary>
[Key(1)]
public string CmdCode { get; set; }
/// <summary>
/// 目标对象 ID
/// <para>示例: 摄像头ID "101",或者系统级指令填 "SYSTEM"</para>
/// </summary>
[Key(2)]
public string TargetId { get; set; }
/// <summary>
/// 业务参数 (JSON 字符串)
/// <para>根据 CmdCode 的不同,反序列化为不同的 DTO (如 PtzControlDto)</para>
/// </summary>
[Key(3)]
public string JsonParams { get; set; }
#endregion
@@ -36,11 +52,13 @@ namespace SHH.Contracts
/// 请求追踪 ID (UUID)
/// <para>核心字段:用于实现异步等待 (await)。回执包必须携带此 ID。</para>
/// </summary>
[Key(4)]
public string RequestId { get; set; } = Guid.NewGuid().ToString("N");
/// <summary>
/// 发送时间戳
/// </summary>
[Key(5)]
public DateTime Timestamp { get; set; } = DateTime.Now;
#endregion
@@ -52,6 +70,7 @@ namespace SHH.Contracts
/// <para>true: 发送端会 await 等待结果 (默认)</para>
/// <para>false: 发后即忘 (Fire-and-Forget),服务端收到后不回发任何消息,减少带宽</para>
/// </summary>
[Key(6)]
public bool RequireAck { get; set; } = true;
/// <summary>
@@ -60,12 +79,14 @@ namespace SHH.Contracts
/// <para>1, 2...: 第N次重试</para>
/// <para>服务端据此判断是否需要查重 (幂等性处理)</para>
/// </summary>
[Key(7)]
public int RetryCount { get; set; } = 0;
/// <summary>
/// 消息过期时间 (Unix时间戳)
/// <para>如果接收端收到时已经超过此时间,直接丢弃,不处理也不回复</para>
/// </summary>
[Key(8)]
public long ExpireTime { get; set; }
#endregion

View File

@@ -0,0 +1,49 @@
using MessagePack;
using System.Collections.Generic;
namespace SHH.Contracts
{
/// <summary>
/// [控制面] 状态全量快照包
/// </summary>
[MessagePackObject]
public class StatusBatchPayload
{
// [新增] 协议类型标识 (人工可读)
// 建议值: "STATUS_BATCH" 或 "设备状态全量包"
[Key(0)]
public string Protocol { get; set; } = "STATUS_BATCH";
[Key(1)]
public List<StatusEventPayload> Items { get; set; }
= new List<StatusEventPayload>();
[Key(2)]
public long Timestamp { get; set; }
}
/// <summary>
/// [控制面] 设备状态变更通知包
/// </summary>
[MessagePackObject]
public class StatusEventPayload
{
[Key(0)]
public string CameraId { get; set; }
/// <summary>
/// true: 上线/活跃, false: 离线/超时
/// </summary>
[Key(1)]
public bool IsOnline { get; set; }
/// <summary>
/// 变更原因 (e.g. "Ping Success", "Frame Timeout")
/// </summary>
[Key(2)]
public string Reason { get; set; }
[Key(3)]
public long Timestamp { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace SHH.Contracts
{
public static class ProtocolHeaders
{
// 核心协议头定义
public const string ServerRegister = "SERVER_REGISTER";
public const string StatusBatch = "STATUS_BATCH";
public const string Command = "COMMAND";
public const string CommandResult = "COMMAND_RESULT";
public const string SyncCamera = "Sync_Camera";
}
}

View File

@@ -1,4 +1,5 @@
using System;
using MessagePack;
using System;
namespace SHH.Contracts
{
@@ -6,24 +7,38 @@ namespace SHH.Contracts
/// 服务端身份注册信息 (DTO)
/// <para>用于服务端主动连上客户端后,上报自身的端口和身份信息</para>
/// </summary>
public class ServerRegistrationDto
[MessagePackObject]
public class RegisterPayload
{
#region --- 0. ---
/// <summary>
/// 协议类型标识 (人工可读)
/// </summary>
[Key(0)]
public string Protocol { get; set; } = ProtocolHeaders.ServerRegister;
#endregion
#region --- 1. ---
/// <summary>
/// 进程 ID (用于区分同一台机器上的多个实例)
/// </summary>
[Key(1)]
public int ProcessId { get; set; }
/// <summary>
/// 实例唯一标识符
/// <para>启动时通过命令行传入,例如 "Gateway_Factory_A"</para>
/// </summary>
[Key(2)]
public string InstanceId { get; set; }
/// <summary>
/// 服务端版本号
/// </summary>
[Key(3)]
public string Version { get; set; } = "1.0.0";
#endregion
@@ -34,22 +49,26 @@ namespace SHH.Contracts
/// 服务端所在的局域网 IP
/// <para>客户端无法直接连接此IP(因为可能是内网),但运维人员需要知道</para>
/// </summary>
[Key(4)]
public string ServerIp { get; set; }
/// <summary>
/// WebAPI 监听端口 (HTTP)
/// <para>用于运维人员打开 Swagger 进行调试</para>
/// </summary>
[Key(5)]
public int WebApiPort { get; set; }
/// <summary>
/// 视频流端口 (ZeroMQ Publisher/Push)
/// </summary>
[Key(6)]
public int VideoPort { get; set; }
/// <summary>
/// 指令流端口 (ZeroMQ Response)
/// </summary>
[Key(7)]
public int CmdPort { get; set; }
#endregion
@@ -59,11 +78,13 @@ namespace SHH.Contracts
/// <summary>
/// 启动时间
/// </summary>
[Key(8)]
public DateTime StartTime { get; set; }
/// <summary>
/// 描述信息 (可选)
/// </summary>
[Key(9)]
public string Description { get; set; }
#endregion

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using MessagePack;
using Newtonsoft.Json;
using System.Collections.Generic;
// 注意:如果不想依赖 Newtonsoft也可以用 System.Text.Json但 Newtonsoft 在 Std 2.0 中兼容性更好
namespace SHH.Contracts
@@ -8,6 +8,7 @@ namespace SHH.Contracts
/// <summary>
/// 视频数据传输契约(纯净版 POCO
/// </summary>
[MessagePackObject]
public class VideoPayload
{
public VideoPayload()
@@ -18,35 +19,49 @@ namespace SHH.Contracts
#region --- 1. (Metadata) ---
[Key(0)]
public string CameraId { get; set; }
/// <summary>
/// 采集时间戳 (Unix 毫秒)
/// </summary>
[Key(1)]
public long CaptureTimestamp { get; set; }
/// <summary>
/// 分发时间戳 (Unix 毫秒)
/// </summary>
[Key(2)]
public long DispatchTimestamp { get; set; }
[Key(3)]
public int OriginalWidth { get; set; }
[Key(4)]
public int OriginalHeight { get; set; }
[Key(5)]
public int TargetWidth { get; set; }
[Key(6)]
public int TargetHeight { get; set; }
public List<string> SubscriberIds { get; }
[Key(7)]
public List<string> SubscriberIds { get; set; }
public Dictionary<string, object> Diagnostics { get; }
[Key(8)]
public Dictionary<string, object> Diagnostics { get; set; }
/// <summary>
/// 指示标志:是否存在原始图
/// </summary>
[Key(9)]
public bool HasOriginalImage { get; set; }
/// <summary>
/// 指示标志:是否存在处理图
/// </summary>
[Key(10)]
public bool HasTargetImage { get; set; }
#endregion
@@ -55,9 +70,11 @@ namespace SHH.Contracts
// 标记 JsonIgnore防止被错误序列化
[JsonIgnore]
[IgnoreMember]
public byte[] OriginalImageBytes { get; set; }
[JsonIgnore]
[IgnoreMember]
public byte[] TargetImageBytes { get; set; }
#endregion

View File

@@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="3.1.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>