完善契约与客户端、服务端的收发代码
This commit is contained in:
73
SHH.Contracts/CommandPayload.cs
Normal file
73
SHH.Contracts/CommandPayload.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用指令请求载体 (Request)
|
||||
/// <para>用于 NetMQ 的 Request-Reply 或 Router-Dealer 模式</para>
|
||||
/// </summary>
|
||||
public class CommandPayload
|
||||
{
|
||||
#region --- 核心路由信息 ---
|
||||
|
||||
/// <summary>
|
||||
/// 指令代码 (路由键)
|
||||
/// <para>示例: "PTZ", "RECORD_START", "SERVER_REGISTER"</para>
|
||||
/// </summary>
|
||||
public string CmdCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标对象 ID
|
||||
/// <para>示例: 摄像头ID "101",或者系统级指令填 "SYSTEM"</para>
|
||||
/// </summary>
|
||||
public string TargetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 业务参数 (JSON 字符串)
|
||||
/// <para>根据 CmdCode 的不同,反序列化为不同的 DTO (如 PtzControlDto)</para>
|
||||
/// </summary>
|
||||
public string JsonParams { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 追踪与元数据 ---
|
||||
|
||||
/// <summary>
|
||||
/// 请求追踪 ID (UUID)
|
||||
/// <para>核心字段:用于实现异步等待 (await)。回执包必须携带此 ID。</para>
|
||||
/// </summary>
|
||||
public string RequestId { get; set; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
/// <summary>
|
||||
/// 发送时间戳
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 可靠性控制字段 (QoS) ---
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要回执 (ACK)
|
||||
/// <para>true: 发送端会 await 等待结果 (默认)</para>
|
||||
/// <para>false: 发后即忘 (Fire-and-Forget),服务端收到后不回发任何消息,减少带宽</para>
|
||||
/// </summary>
|
||||
public bool RequireAck { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 重试计数器
|
||||
/// <para>0: 首次发送</para>
|
||||
/// <para>1, 2...: 第N次重试</para>
|
||||
/// <para>服务端据此判断是否需要查重 (幂等性处理)</para>
|
||||
/// </summary>
|
||||
public int RetryCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 消息过期时间 (Unix时间戳)
|
||||
/// <para>如果接收端收到时已经超过此时间,直接丢弃,不处理也不回复</para>
|
||||
/// </summary>
|
||||
public long ExpireTime { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
65
SHH.Contracts/CommandResult.cs
Normal file
65
SHH.Contracts/CommandResult.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用指令执行结果 (Response)
|
||||
/// </summary>
|
||||
public class CommandResult
|
||||
{
|
||||
#region --- 核心匹配信息 ---
|
||||
|
||||
/// <summary>
|
||||
/// 回执 ID (必须与请求包的 RequestId 一致)
|
||||
/// <para>客户端靠这个 ID 来找到对应的 await Task</para>
|
||||
/// </summary>
|
||||
public string RequestId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 执行结果 ---
|
||||
|
||||
/// <summary>
|
||||
/// 执行是否成功
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结果消息 (成功提示或错误原因)
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回的数据 (JSON 或 Base64 字符串)
|
||||
/// <para>示例: 截图的 Base64,或者查询到的设备列表 JSON</para>
|
||||
/// </summary>
|
||||
public string Data { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 性能统计 ---
|
||||
|
||||
/// <summary>
|
||||
/// 全链路耗时 (毫秒)
|
||||
/// <para>从客户端发出指令,到收到服务端回执的总时长</para>
|
||||
/// <para>注意:该字段由客户端收到回执后自动计算填充,服务端不需要赋值</para>
|
||||
/// </summary>
|
||||
public double ElapsedMilliseconds { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 快捷构造方法 ---
|
||||
|
||||
/// <summary>
|
||||
/// 快速创建一个成功的回执
|
||||
/// </summary>
|
||||
public static CommandResult Ok(string msg = "OK", string data = null)
|
||||
=> new CommandResult { Success = true, Message = msg, Data = data };
|
||||
|
||||
/// <summary>
|
||||
/// 快速创建一个失败的回执
|
||||
/// </summary>
|
||||
public static CommandResult Fail(string msg)
|
||||
=> new CommandResult { Success = false, Message = msg };
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
71
SHH.Contracts/ServerRegistrationDto.cs
Normal file
71
SHH.Contracts/ServerRegistrationDto.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务端身份注册信息 (DTO)
|
||||
/// <para>用于服务端主动连上客户端后,上报自身的端口和身份信息</para>
|
||||
/// </summary>
|
||||
public class ServerRegistrationDto
|
||||
{
|
||||
#region --- 1. 身份标识 ---
|
||||
|
||||
/// <summary>
|
||||
/// 进程 ID (用于区分同一台机器上的多个实例)
|
||||
/// </summary>
|
||||
public int ProcessId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实例唯一标识符
|
||||
/// <para>启动时通过命令行传入,例如 "Gateway_Factory_A"</para>
|
||||
/// </summary>
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务端版本号
|
||||
/// </summary>
|
||||
public string Version { get; set; } = "1.0.0";
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2. 网络诊断信息 (用于运维) ---
|
||||
|
||||
/// <summary>
|
||||
/// 服务端所在的局域网 IP
|
||||
/// <para>客户端无法直接连接此IP(因为可能是内网),但运维人员需要知道</para>
|
||||
/// </summary>
|
||||
public string ServerIp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// WebAPI 监听端口 (HTTP)
|
||||
/// <para>用于运维人员打开 Swagger 进行调试</para>
|
||||
/// </summary>
|
||||
public int WebApiPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 视频流端口 (ZeroMQ Publisher/Push)
|
||||
/// </summary>
|
||||
public int VideoPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指令流端口 (ZeroMQ Response)
|
||||
/// </summary>
|
||||
public int CmdPort { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3. 运行时状态 ---
|
||||
|
||||
/// <summary>
|
||||
/// 启动时间
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述信息 (可选)
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
@@ -9,57 +9,90 @@ namespace SHH.Contracts
|
||||
/// </summary>
|
||||
public class VideoPayload
|
||||
{
|
||||
public List<string> SubscriberIds { get; } = new List<string>(16);
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="VideoPayload"/> 类的新实例。
|
||||
/// </summary>
|
||||
public VideoPayload()
|
||||
{
|
||||
// 预分配一个容量为 16 的列表,以减少内存分配和垃圾回收的压力。
|
||||
SubscriberIds = new List<string>(16);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 1. 基础元数据 (将被序列化到 JSON)
|
||||
// ==========================================
|
||||
|
||||
public string CameraId { get; set; } // 摄像头唯一标记
|
||||
|
||||
// 时间信息 (建议使用 DateTime,调试看日志更直观)
|
||||
public DateTime CaptureTime { get; set; } // 采集时间 (SDK产生图的时间)
|
||||
public DateTime DispatchTime { get; set; } // 分发时间 (Server发出图的时间)
|
||||
|
||||
// ==========================================
|
||||
// 2. 图像规格信息
|
||||
// ==========================================
|
||||
|
||||
public int OriginalWidth { get; set; } // 原始宽度
|
||||
public int OriginalHeight { get; set; } // 原始高度
|
||||
|
||||
public int TargetWidth { get; set; } // 目标/处理后宽度
|
||||
public int TargetHeight { get; set; } // 目标/处理后高度
|
||||
|
||||
// ==========================================
|
||||
// 3. 核心二进制数据 (严禁序列化到 JSON)
|
||||
// ==========================================
|
||||
#region --- 元数据 (Metadata) ---
|
||||
|
||||
/// <summary>
|
||||
/// 原始图像数据 (例如海康SDK出来的原始 JPG)
|
||||
/// JsonIgnore 防止误操作导致序列化性能崩塌
|
||||
/// 获取订阅了此帧数据的客户端ID列表。
|
||||
/// </summary>
|
||||
public List<string> SubscriberIds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置摄像头的唯一标识符。
|
||||
/// </summary>
|
||||
public string CameraId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置图像的采集时间,即从SDK获取到图像数据的时间。
|
||||
/// </summary>
|
||||
public DateTime CaptureTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置图像的分发时间,即服务器准备将此帧数据发送给客户端的时间。
|
||||
/// </summary>
|
||||
public DateTime DispatchTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置图像的原始宽度。
|
||||
/// </summary>
|
||||
public int OriginalWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置图像的原始高度。
|
||||
/// </summary>
|
||||
public int OriginalHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置经过处理后的目标图像宽度。
|
||||
/// </summary>
|
||||
public int TargetWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置经过处理后的目标图像高度。
|
||||
/// </summary>
|
||||
public int TargetHeight { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 核心二进制数据 ---
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置原始图像的二进制数据(例如,从SDK直接获取的JPG或YUV数据)。
|
||||
/// 此属性被标记为 <see cref="JsonIgnore"/>,以防止在序列化元数据时将其包含在内,从而避免严重的性能问题。
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public byte[] OriginalImageBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 处理后的目标图像 (例如 Yolo 画框后的图,或者缩放后的图)
|
||||
/// 可为空
|
||||
/// 获取或设置经过处理后的目标图像的二进制数据(例如,经过缩放、画框或其他AI处理后的图像)。
|
||||
/// 此属性可为空,表示此帧可能只包含原始图像或没有图像数据。
|
||||
/// 同样,此属性也被标记为 <see cref="JsonIgnore"/>。
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public byte[] TargetImageBytes { get; set; }
|
||||
|
||||
// ==========================================
|
||||
// 4. 辅助方法
|
||||
// ==========================================
|
||||
#endregion
|
||||
|
||||
#region --- 序列化与反序列化辅助方法 ---
|
||||
|
||||
/// <summary>
|
||||
/// 仅获取元数据的 JSON 字符串
|
||||
/// 将当前对象的元数据序列化为一个纯净的 JSON 字符串。
|
||||
/// 此方法会自动忽略所有二进制数据(<see cref="OriginalImageBytes"/> 和 <see cref="TargetImageBytes"/>)。
|
||||
/// </summary>
|
||||
/// <returns>包含元数据的 JSON 字符串。</returns>
|
||||
public string GetMetadataJson()
|
||||
{
|
||||
// 创建一个纯净的匿名对象用于序列化
|
||||
var meta = new
|
||||
// 创建一个匿名对象,该对象仅包含需要被序列化的元数据字段。
|
||||
// 这比直接序列化整个对象更安全、更高效。
|
||||
var metadata = new
|
||||
{
|
||||
CameraId,
|
||||
CaptureTime,
|
||||
@@ -69,18 +102,24 @@ namespace SHH.Contracts
|
||||
TargetWidth,
|
||||
TargetHeight,
|
||||
SubscriberIds,
|
||||
// 标记一下是否有目标图,方便接收端判断要不要读第3帧
|
||||
// 附加一个标志,指示此载荷中是否包含目标图像数据,以便接收端进行判断。
|
||||
HasTargetImage = (TargetImageBytes != null && TargetImageBytes.Length > 0)
|
||||
};
|
||||
return JsonConvert.SerializeObject(meta);
|
||||
return JsonConvert.SerializeObject(metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 JSON 还原元数据 (还原出来的对象 ImageBytes 默认为空,需后续填充)
|
||||
/// 从一个 JSON 字符串反序列化,创建一个新的 <see cref="VideoPayload"/> 对象。
|
||||
/// 注意:反序列化后,对象中的二进制图像数据(<see cref="OriginalImageBytes"/> 和 <see cref="TargetImageBytes"/>)将为 null,
|
||||
/// 需要在后续步骤中手动填充。
|
||||
/// </summary>
|
||||
/// <param name="json">包含元数据的 JSON 字符串。</param>
|
||||
/// <returns>一个新的 <see cref="VideoPayload"/> 对象,其元数据已填充。</returns>
|
||||
public static VideoPayload FromMetadataJson(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<VideoPayload>(json);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user