在 AiVideo 中能看到图像
增加了在线状态同步逻辑
This commit is contained in:
@@ -1,174 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
// ==============================================================================
|
||||
// 1. 物理与运行配置 DTO (对应 CRUD 操作)
|
||||
// 用于设备新增/全量配置查询,包含基础身份、连接信息、运行参数等全量字段
|
||||
// ==============================================================================
|
||||
public class CameraConfigDto
|
||||
{
|
||||
// --- 基础身份 (Identity) ---
|
||||
/// <summary>
|
||||
/// 设备唯一标识
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "设备ID不能为空")]
|
||||
[Range(1, long.MaxValue, ErrorMessage = "设备ID必须为正整数")]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备友好名称
|
||||
/// </summary>
|
||||
[MaxLength(64, ErrorMessage = "设备名称长度不能超过64个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 摄像头品牌类型 (0:HikVision, 1:Dahua, 2:RTSP...)
|
||||
/// </summary>
|
||||
[Range(0, 10, ErrorMessage = "品牌类型值必须在0-10范围内")]
|
||||
public int Brand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备安装位置描述
|
||||
/// </summary>
|
||||
[MaxLength(128, ErrorMessage = "安装位置长度不能超过128个字符")]
|
||||
public string Location { get; set; }
|
||||
|
||||
// --- 主板关联信息 (Metadata) ---
|
||||
|
||||
/// <summary>
|
||||
/// 关联主板IP地址
|
||||
/// </summary>
|
||||
[RegularExpression(@"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)?$",
|
||||
ErrorMessage = "请输入合法的IPv4地址")]
|
||||
public string MainboardIp { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 关联主板端口
|
||||
/// </summary>
|
||||
[Range(0, 65535, ErrorMessage = "主板端口号必须在1-65535范围内")]
|
||||
public int MainboardPort { get; set; } = 0;
|
||||
|
||||
// --- 核心连接 (Connectivity) - 修改此类参数触发冷重启 ---
|
||||
|
||||
/// <summary>
|
||||
/// 摄像头IP地址
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "IP地址不能为空")]
|
||||
[RegularExpression(@"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$",
|
||||
ErrorMessage = "请输入合法的IPv4地址")]
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录用户名
|
||||
/// </summary>
|
||||
[MaxLength(32, ErrorMessage = "用户名长度不能超过32个字符")]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录密码
|
||||
/// </summary>
|
||||
[MaxLength(64, ErrorMessage = "密码长度不能超过64个字符")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SDK端口 (如海康默认8000)
|
||||
/// </summary>
|
||||
[Range(1, 65535, ErrorMessage = "端口号必须在1-65535范围内")]
|
||||
public ushort Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通道号 (通常为1)
|
||||
/// </summary>
|
||||
[Range(0, 256, ErrorMessage = "通道号必须在0-256范围内")]
|
||||
public int ChannelIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 码流类型 (0:主码流, 1:子码流)
|
||||
/// </summary>
|
||||
[Range(0, 1, ErrorMessage = "码流类型只能是0(主码流)或1(子码流)")]
|
||||
public int StreamType { get; set; }
|
||||
|
||||
// 渲染句柄 (通常下发时为0,由本地窗口绑定时再指定,或者此处仅作占位)
|
||||
public long RenderHandle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// RTSP流路径 (备用或非SDK模式使用)
|
||||
/// </summary>
|
||||
[MaxLength(256, ErrorMessage = "RTSP地址长度不能超过256个字符")]
|
||||
public string RtspPath { get; set; }
|
||||
|
||||
// --- 运行时参数 (Runtime Options) - 支持热更新 ---
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用灰度图 (用于AI分析场景加速)
|
||||
/// </summary>
|
||||
public bool UseGrayscale { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用图像增强 (去噪/锐化等)
|
||||
/// </summary>
|
||||
public bool EnhanceImage { get; set; } = true;
|
||||
|
||||
// --- 画面变换 (Transform) - 支持热更新 ---
|
||||
/// <summary>
|
||||
/// 是否允许图像压缩 (降低带宽占用)
|
||||
/// </summary>
|
||||
public bool AllowCompress { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许图像放大 (提升渲染质量)
|
||||
/// </summary>
|
||||
public bool AllowExpand { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 目标分辨率 (格式如 1920x1080,空则保持原图)
|
||||
/// </summary>
|
||||
[RegularExpression(@"^\d+x\d+$", ErrorMessage = "分辨率格式必须为 宽度x高度 (如 1920x1080)")]
|
||||
public string TargetResolution { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 随配置一并下发的自动订阅请求
|
||||
/// </summary>
|
||||
public List<CameraConfigSubscribeDto> AutoSubscriptions { get; set; }
|
||||
= new List<CameraConfigSubscribeDto>();
|
||||
|
||||
/// <summary>
|
||||
/// 是否立即执行
|
||||
/// </summary>
|
||||
public bool ImmediateExecution { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅项
|
||||
/// </summary>
|
||||
public class CameraConfigSubscribeDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 订阅标识
|
||||
/// </summary>
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订阅业务类型 SubscriptionType
|
||||
/// </summary>
|
||||
public int Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 要求的帧率:8帧或1帧
|
||||
/// </summary>
|
||||
public int TargetFps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Memo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要高清晰度
|
||||
/// </summary>
|
||||
public bool NeedHighDefinition { get; set; }
|
||||
= false;
|
||||
}
|
||||
}
|
||||
94
SHH.Contracts/Commands/CommandPayload.cs
Normal file
94
SHH.Contracts/Commands/CommandPayload.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using MessagePack;
|
||||
using System;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用指令请求载体 (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
|
||||
|
||||
#region --- 追踪与元数据 ---
|
||||
|
||||
/// <summary>
|
||||
/// 请求追踪 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
|
||||
|
||||
#region --- 可靠性控制字段 (QoS) ---
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要回执 (ACK)
|
||||
/// <para>true: 发送端会 await 等待结果 (默认)</para>
|
||||
/// <para>false: 发后即忘 (Fire-and-Forget),服务端收到后不回发任何消息,减少带宽</para>
|
||||
/// </summary>
|
||||
[Key(6)]
|
||||
public bool RequireAck { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 重试计数器
|
||||
/// <para>0: 首次发送</para>
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
49
SHH.Contracts/Commands/DeviceStatusEvent.cs
Normal file
49
SHH.Contracts/Commands/DeviceStatusEvent.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
13
SHH.Contracts/Commands/ProtocolHeaders.cs
Normal file
13
SHH.Contracts/Commands/ProtocolHeaders.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
92
SHH.Contracts/Commands/RegisterPayload.cs
Normal file
92
SHH.Contracts/Commands/RegisterPayload.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using MessagePack;
|
||||
using System;
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务端身份注册信息 (DTO)
|
||||
/// <para>用于服务端主动连上客户端后,上报自身的端口和身份信息</para>
|
||||
/// </summary>
|
||||
[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
|
||||
|
||||
#region --- 2. 网络诊断信息 (用于运维) ---
|
||||
|
||||
/// <summary>
|
||||
/// 服务端所在的局域网 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
|
||||
|
||||
#region --- 3. 运行时状态 ---
|
||||
|
||||
/// <summary>
|
||||
/// 启动时间
|
||||
/// </summary>
|
||||
[Key(8)]
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述信息 (可选)
|
||||
/// </summary>
|
||||
[Key(9)]
|
||||
public string Description { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
103
SHH.Contracts/Commands/VideoPayload.cs
Normal file
103
SHH.Contracts/Commands/VideoPayload.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
// 注意:如果不想依赖 Newtonsoft,也可以用 System.Text.Json,但 Newtonsoft 在 Std 2.0 中兼容性更好
|
||||
|
||||
namespace SHH.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// 视频数据传输契约(纯净版 POCO)
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public class VideoPayload
|
||||
{
|
||||
public VideoPayload()
|
||||
{
|
||||
SubscriberIds = new List<string>(16);
|
||||
Diagnostics = new Dictionary<string, object>(4);
|
||||
}
|
||||
|
||||
#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; }
|
||||
|
||||
[Key(7)]
|
||||
public List<string> SubscriberIds { get; set; }
|
||||
|
||||
[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
|
||||
|
||||
#region --- 2. 二进制数据 (Binary) ---
|
||||
|
||||
// 标记 JsonIgnore,防止被错误序列化
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public byte[] OriginalImageBytes { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public byte[] TargetImageBytes { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3. 辅助方法 (仅保留 JSON 逻辑) ---
|
||||
|
||||
/// <summary>
|
||||
/// 获取纯元数据的 JSON 字符串
|
||||
/// </summary>
|
||||
public string GetMetadataJson()
|
||||
{
|
||||
// 在序列化前自动更新标志位,防止逻辑不同步
|
||||
this.HasOriginalImage = (OriginalImageBytes != null && OriginalImageBytes.Length > 0);
|
||||
this.HasTargetImage = (TargetImageBytes != null && TargetImageBytes.Length > 0);
|
||||
|
||||
return JsonConvert.SerializeObject(this);
|
||||
}
|
||||
|
||||
public static VideoPayload FromMetadataJson(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<VideoPayload>(json);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user