176 lines
6.9 KiB
C#
176 lines
6.9 KiB
C#
|
|
namespace SHH.CameraSdk;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 增强型设备元数据中心 (V3.3.1 修复版)
|
|||
|
|
/// 核心职责:
|
|||
|
|
/// <para>1. 封装设备的硬件参数、通道能力、功能集,提供能力自发现</para>
|
|||
|
|
/// <para>2. 支持元数据同步与差异对比,指导上层模块执行差异化处理</para>
|
|||
|
|
/// <para>3. 存储运维指标与 SDK 原生句柄,支撑故障诊断与性能调优</para>
|
|||
|
|
/// 设计特性:只读优先,通过版本号标记同步状态,避免并发修改冲突
|
|||
|
|
/// </summary>
|
|||
|
|
public class DeviceMetadata
|
|||
|
|
{
|
|||
|
|
#region --- 1. 设备级身份信息 (Identity) ---
|
|||
|
|
|
|||
|
|
/// <summary> 设备型号名称(如 DS-2CD3T47G2-LIU) </summary>
|
|||
|
|
public string ModelName { get; init; } = "Unknown";
|
|||
|
|
|
|||
|
|
/// <summary> 设备唯一序列号(全局唯一,用于设备溯源) </summary>
|
|||
|
|
public string SerialNumber { get; init; } = string.Empty;
|
|||
|
|
|
|||
|
|
/// <summary> 固件/系统版本号(用于判断 SDK 兼容性) </summary>
|
|||
|
|
public string FirmwareVersion { get; init; } = string.Empty;
|
|||
|
|
|
|||
|
|
/// <summary> 所属厂商/品牌(决定驱动适配逻辑) </summary>
|
|||
|
|
public DeviceBrand Brand { get; init; } = DeviceBrand.Unknown;
|
|||
|
|
|
|||
|
|
/// <summary> 元数据版本号(本地刷新计数,每次同步自增) </summary>
|
|||
|
|
public long Version { get; private set; }
|
|||
|
|
|
|||
|
|
/// <summary> 最后同步时间(标记元数据的最新有效时刻) </summary>
|
|||
|
|
public DateTime LastSyncedAt { get; private set; }
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region --- 2. 级联能力模型 (Cascaded Capabilities) ---
|
|||
|
|
|
|||
|
|
private readonly ReadOnlyCollection<ChannelMetadata> _channels;
|
|||
|
|
|
|||
|
|
/// <summary> 通道元数据集合(只读,防止外部篡改) </summary>
|
|||
|
|
public ReadOnlyCollection<ChannelMetadata> Channels
|
|||
|
|
{
|
|||
|
|
get => _channels;
|
|||
|
|
init => _channels = value ?? new ReadOnlyCollection<ChannelMetadata>(new List<ChannelMetadata>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary> 设备总通道数量(IPC 通常为 1,NVR 为接入路数) </summary>
|
|||
|
|
public int ChannelCount => Channels.Count;
|
|||
|
|
|
|||
|
|
/// <summary> 索引器:通过通道号快速获取对应通道的能力描述 </summary>
|
|||
|
|
/// <param name="channelIndex">物理通道号</param>
|
|||
|
|
/// <returns>通道元数据 / 不存在则返回 null</returns>
|
|||
|
|
public ChannelMetadata? this[int channelIndex] =>
|
|||
|
|
Channels.FirstOrDefault(c => c.ChannelIndex == channelIndex);
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region --- 3. 运维指标与非托管句柄 ---
|
|||
|
|
|
|||
|
|
/// <summary> 设备实时健康度字典(如 CPU 使用率、温度、内存占用等) </summary>
|
|||
|
|
public Dictionary<string, object> HealthMetrics { get; init; } = new();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 厂商 SDK 原始句柄/结构体快照
|
|||
|
|
/// <para>注意事项:</para>
|
|||
|
|
/// <para>1. 标记 [JsonIgnore] 防止序列化非托管指针导致程序崩溃</para>
|
|||
|
|
/// <para>2. 仅用于驱动层与 SDK 交互,上层业务禁止直接操作</para>
|
|||
|
|
/// </summary>
|
|||
|
|
[JsonIgnore]
|
|||
|
|
public object? NativeHandle { get; init; }
|
|||
|
|
|
|||
|
|
/// <summary> 厂商扩展标签(如设备位置、安装时间、责任人等自定义信息) </summary>
|
|||
|
|
public Dictionary<string, string> Tags { get; init; } = new();
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region --- 4. 构造与同步 (Constructor & Sync) ---
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 默认构造函数(用于 BaseVideoSource 初始状态,无通道数据)
|
|||
|
|
/// </summary>
|
|||
|
|
public DeviceMetadata() : this(Enumerable.Empty<ChannelMetadata>()) { }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 完整构造函数(初始化通道元数据集合)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="channels">通道元数据列表</param>
|
|||
|
|
public DeviceMetadata(IEnumerable<ChannelMetadata> channels)
|
|||
|
|
{
|
|||
|
|
// 转换为只读集合,确保通道数据不可变
|
|||
|
|
_channels = new ReadOnlyCollection<ChannelMetadata>(channels?.ToList() ?? new List<ChannelMetadata>());
|
|||
|
|
// 标记初始同步状态
|
|||
|
|
MarkSynced();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 标记元数据同步完成
|
|||
|
|
/// <para>作用:更新版本号与同步时间,用于差异对比的基准判断</para>
|
|||
|
|
/// </summary>
|
|||
|
|
public void MarkSynced()
|
|||
|
|
{
|
|||
|
|
Version++;
|
|||
|
|
LastSyncedAt = DateTime.Now;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region --- 5. 业务逻辑辅助方法 (Business Helpers) ---
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 校验动态流配置的合法性(基于设备能力)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="options">待校验的动态配置项</param>
|
|||
|
|
/// <param name="errorMessage">校验失败时的详细原因</param>
|
|||
|
|
/// <returns>合法返回 true,非法返回 false</returns>
|
|||
|
|
public bool ValidateOptions(DynamicStreamOptions options, out string errorMessage)
|
|||
|
|
{
|
|||
|
|
errorMessage = string.Empty;
|
|||
|
|
if (options == null) return true;
|
|||
|
|
|
|||
|
|
// 示例校验规则:云台控制权限校验
|
|||
|
|
if (options.VendorExtensions?.ContainsKey("PtzAction") == true
|
|||
|
|
&& !Channels.Any(c => c.SupportPtz))
|
|||
|
|
{
|
|||
|
|
errorMessage = "该设备物理硬件不支持云台控制功能";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 可扩展其他校验规则:如分辨率合法性、码流类型支持性等
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 元数据差异比对逻辑(用于 BaseVideoSource.RefreshMetadataAsync 方法)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="other">最新拉取的设备元数据</param>
|
|||
|
|
/// <returns>元数据差异描述符</returns>
|
|||
|
|
public MetadataDiff CompareWith(DeviceMetadata other)
|
|||
|
|
{
|
|||
|
|
// 入参防护:对比对象为空则返回无差异
|
|||
|
|
if (other == null) return MetadataDiff.None;
|
|||
|
|
|
|||
|
|
return new MetadataDiff
|
|||
|
|
{
|
|||
|
|
// 1. 基础信息变更:任意通道名称变化则标记
|
|||
|
|
NameChanged = this.Channels.Any(c =>
|
|||
|
|
{
|
|||
|
|
var targetChannel = other[c.ChannelIndex];
|
|||
|
|
return targetChannel != null && targetChannel.Name != c.Name;
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
// 2. 能力集变更:PTZ/音频/AI 功能支持状态变化则标记
|
|||
|
|
CapabilityChanged = this.Channels.Any(c =>
|
|||
|
|
{
|
|||
|
|
var targetChannel = other[c.ChannelIndex];
|
|||
|
|
if (targetChannel == null) return false;
|
|||
|
|
|
|||
|
|
return targetChannel.SupportPtz != c.SupportPtz
|
|||
|
|
|| targetChannel.SupportAudioIn != c.SupportAudioIn
|
|||
|
|
|| targetChannel.SupportAiAnalysis != c.SupportAiAnalysis;
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
// 3. 致命变更:品牌不一致或通道数量变化(设备更换/替换场景)
|
|||
|
|
IsMajorChange = this.Brand != other.Brand || this.ChannelCount != other.ChannelCount,
|
|||
|
|
|
|||
|
|
// 4. 分辨率配置变更:任意通道的分辨率档位数量变化则标记
|
|||
|
|
ResolutionProfilesChanged = this.Channels.Any(c =>
|
|||
|
|
{
|
|||
|
|
var targetChannel = other[c.ChannelIndex];
|
|||
|
|
return targetChannel != null
|
|||
|
|
&& targetChannel.SupportedResolutions.Count != c.SupportedResolutions.Count;
|
|||
|
|
})
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|