海康摄像头取流示例初始签入
This commit is contained in:
226
SHH.CameraSdk/Abstractions/Errors/CameraErrorCode.cs
Normal file
226
SHH.CameraSdk/Abstractions/Errors/CameraErrorCode.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// 工业级相机归一化错误码 (修正全量版)
|
||||
/// 职责:跨厂家建立统一故障语义,支撑 HikErrorMapper 等驱动层的精准映射。
|
||||
/// </summary>
|
||||
public enum CameraErrorCode
|
||||
{
|
||||
[Description("操作成功")]
|
||||
Success = 0,
|
||||
|
||||
#region --- 1000-1499 运行环境与 SDK 基础故障 ---
|
||||
|
||||
[Description("SDK 未初始化")]
|
||||
SdkNotInitialized = 1000,
|
||||
|
||||
[Description("SDK 资源分配错误或本地内存不足")]
|
||||
LocalResourceError = 1001,
|
||||
|
||||
[Description("加载插件/组件失败:缺少 DLL 或依赖库")]
|
||||
ComponentLoadFailed = 1002,
|
||||
|
||||
[Description("组件版本不匹配")]
|
||||
ComponentVersionMismatch = 1003,
|
||||
|
||||
[Description("加载加密库失败(Ope nSSL/LibEay32)")]
|
||||
EncryptionLibError = 1004, // 已补齐 (海康 156)
|
||||
|
||||
[Description("函数调用顺序错误")]
|
||||
FunctionOrderError = 1005,
|
||||
|
||||
[Description("操作系统不支持该功能")]
|
||||
OsNotSupported = 1006,
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 1500-1999 网络通信与协议故障 ---
|
||||
|
||||
[Description("连接设备失败:设备离线")]
|
||||
NetworkUnreachable = 1500,
|
||||
|
||||
[Description("交互超时:网络拥塞或设备响应慢")]
|
||||
Timeout = 1501,
|
||||
|
||||
[Description("数据发送失败")]
|
||||
NetworkSendError = 1502,
|
||||
|
||||
[Description("数据接收失败")]
|
||||
NetworkRecvError = 1503,
|
||||
|
||||
[Description("网络套接字(Socket)异常")]
|
||||
SocketError = 1504,
|
||||
|
||||
[Description("IP 地址冲突")]
|
||||
IpConflict = 1505,
|
||||
|
||||
[Description("端口池耗尽或端口复用失败")]
|
||||
PortPoolExhausted = 1506,
|
||||
|
||||
[Description("连接已失效或未建立")]
|
||||
InvalidLink = 1507, // 已补齐 (海康 188)
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2000-2499 身份认证与权限管理 ---
|
||||
|
||||
[Description("用户名或密码错误")]
|
||||
InvalidCredentials = 2000,
|
||||
|
||||
[Description("用户权限不足")]
|
||||
AccessDenied = 2001,
|
||||
|
||||
[Description("用户不存在")]
|
||||
UserNotExist = 2002,
|
||||
|
||||
[Description("账号已被锁定(多次尝试失败)")]
|
||||
AccountLocked = 2003,
|
||||
|
||||
[Description("登录人数已达上限")]
|
||||
MaxUserExceeded = 2004,
|
||||
|
||||
[Description("会话已过期或已被强行踢出")]
|
||||
SessionExpired = 2005,
|
||||
|
||||
[Description("用户正在使用中(如正在对讲/升级)")]
|
||||
UserInUse = 2006, // 已补齐 (海康 74)
|
||||
|
||||
[Description("登录版本过低(不支持该协议)")]
|
||||
LoginVersionLow = 2007, // 已补齐 (海康 155)
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2500-2999 设备资源与负载限制 ---
|
||||
|
||||
[Description("设备连接数已达上限")]
|
||||
MaxConnectionsReached = 2500,
|
||||
|
||||
[Description("设备资源不足或内部忙")]
|
||||
DeviceResourceBusy = 2501,
|
||||
|
||||
[Description("通道接入数达到上限")]
|
||||
MaxQuantityExceeded = 2502,
|
||||
|
||||
[Description("主/子码流路数超限")]
|
||||
MaxStreamExceeded = 2503,
|
||||
|
||||
[Description("设备缓冲区不足/溢出")]
|
||||
DeviceBufferOverflow = 2504,
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3000-3499 视频预览、回放与解码 ---
|
||||
|
||||
[Description("预览失败或通道未编码")]
|
||||
PreviewFailed = 3000,
|
||||
|
||||
[Description("码流封装格式不支持")]
|
||||
StreamTypeNotSupport = 3001,
|
||||
|
||||
[Description("码流数据中断(丢包/心跳丢失)")]
|
||||
StreamInterrupted = 3002,
|
||||
|
||||
[Description("码流已加密(需二次认证)")]
|
||||
StreamEncrypted = 3003,
|
||||
|
||||
[Description("外接 IP 通道离线")]
|
||||
IpChannelOffline = 3004,
|
||||
|
||||
[Description("设备通道异常")]
|
||||
ChannelException = 3005, // 已补齐 (海康 18)
|
||||
|
||||
[Description("播放库(Player SDK)调用失败")]
|
||||
PlayerSdkFailed = 3006, // 已补齐 (海康 51)
|
||||
|
||||
[Description("音频设备忙(声卡被独占)")]
|
||||
AudioDeviceBusy = 3007, // 已补齐 (海康 69)
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3500-3999 存储管理故障 ---
|
||||
|
||||
[Description("存储设备通用错误")]
|
||||
StorageError = 3500,
|
||||
|
||||
[Description("设备无硬盘")]
|
||||
NoDisk = 3501,
|
||||
|
||||
[Description("硬盘已满")]
|
||||
DiskFull = 3502,
|
||||
|
||||
[Description("硬盘状态异常(格式化中或读写错)")]
|
||||
DiskStatusError = 3503,
|
||||
|
||||
[Description("尝试格式化只读硬盘")]
|
||||
DiskReadOnly = 3504,
|
||||
|
||||
[Description("存储池/NAS 目录无效")]
|
||||
StoragePoolError = 3505,
|
||||
|
||||
[Description("写入存储(Flash/文件)失败")]
|
||||
WriteStorageFailed = 3506, // 已补齐 (海康 48, 77)
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 4000-4499 硬件、参数与系统故障 ---
|
||||
|
||||
[Description("硬件内部故障")]
|
||||
HardwareFault = 4000,
|
||||
|
||||
[Description("通道号错误或不存在")]
|
||||
InvalidChannel = 4001,
|
||||
|
||||
[Description("参数错误(空指针或无效值)")]
|
||||
InvalidParameter = 4002,
|
||||
|
||||
[Description("视频信号丢失(黑屏/丢信号)")]
|
||||
VideoSignalLoss = 4003,
|
||||
|
||||
[Description("设备正在重启中")]
|
||||
DeviceRebooting = 4004,
|
||||
|
||||
[Description("需重启生效")]
|
||||
RebootRequired = 4005,
|
||||
|
||||
[Description("时间输入错误")]
|
||||
InvalidTimeInput = 4006, // 已补齐 (海康 32)
|
||||
|
||||
[Description("设备型号或版本不匹配")]
|
||||
DeviceMismatch = 4007, // 已补齐 (海康 80)
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 4500-4999 操作限制与通用状态 ---
|
||||
|
||||
[Description("设备不支持该功能")]
|
||||
NotSupported = 4500, // 已补齐 (海康 23)
|
||||
|
||||
[Description("修改或设置失败")]
|
||||
ModifyFailed = 4501,
|
||||
|
||||
[Description("不支持无阻塞抓图")]
|
||||
CaptureNotSupport = 4502,
|
||||
|
||||
[Description("设备忙")]
|
||||
DeviceBusy = 4503,
|
||||
|
||||
[Description("上次操作未完成")]
|
||||
OperationNotFinished = 4504,
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 9000-9999 系统级故障 ---
|
||||
|
||||
[Description("驱动未实现该功能")]
|
||||
NotImplemented = 9001,
|
||||
|
||||
[Description("程序异常")]
|
||||
ProgramException = 9998,
|
||||
|
||||
[Description("未知错误")]
|
||||
Unknown = 9999
|
||||
|
||||
#endregion
|
||||
}
|
||||
107
SHH.CameraSdk/Abstractions/Errors/CameraException.cs
Normal file
107
SHH.CameraSdk/Abstractions/Errors/CameraException.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// 视频 SDK 统一异常类 (V3.3.1 修复版)
|
||||
/// 核心职责:
|
||||
/// <para>1. 封装标准化错误码、厂商原始错误码、设备品牌信息</para>
|
||||
/// <para>2. 记录异常上下文快照,辅助故障定位与复盘</para>
|
||||
/// 协作关系:
|
||||
/// <para>1. 与 <see cref="HikErrorMapper"/> 配合:实现厂商错误码→标准错误码的转换</para>
|
||||
/// <para>2. 与 <see cref="RecoveryPolicy"/> 配合:提供错误码输入,驱动故障自愈决策</para>
|
||||
/// </summary>
|
||||
public class CameraException : Exception
|
||||
{
|
||||
#region --- 核心异常属性 (Core Exception Properties) ---
|
||||
|
||||
/// <summary>
|
||||
/// 归一化后的标准错误码
|
||||
/// 业务用途:作为 RecoveryPolicy 的决策输入,屏蔽厂商差异
|
||||
/// </summary>
|
||||
public CameraErrorCode ErrorCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 厂商原始错误码(如海康 NET_DVR_GetLastError、大华 SDK 原生错误码)
|
||||
/// 业务用途:厂商文档对照、深度问题排查
|
||||
/// </summary>
|
||||
public int RawErrorCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 发生异常的设备品牌
|
||||
/// 业务用途:区分不同厂商的错误码规则,辅助错误映射
|
||||
/// </summary>
|
||||
public DeviceBrand Brand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 异常发生时的上下文快照(只读集合,防止外部篡改)
|
||||
/// 存储内容:设备IP、通道号、操作参数、SDK句柄、时间戳等案发现场信息
|
||||
/// 业务用途:故障复盘时还原现场,快速定位根因
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, object> Context { get; init; } = new Dictionary<string, object>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 构造函数 (Constructors) ---
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 CameraException 实例
|
||||
/// </summary>
|
||||
/// <param name="errorCode">归一化标准错误码</param>
|
||||
/// <param name="message">异常描述信息</param>
|
||||
/// <param name="brand">设备品牌</param>
|
||||
/// <param name="rawErrorCode">厂商原始错误码(默认 0)</param>
|
||||
/// <param name="innerException">内部异常(默认 null)</param>
|
||||
public CameraException(
|
||||
CameraErrorCode errorCode,
|
||||
string message,
|
||||
DeviceBrand brand,
|
||||
int rawErrorCode = 0,
|
||||
Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Brand = brand;
|
||||
RawErrorCode = rawErrorCode;
|
||||
// 初始化上下文字典为可写的 Dictionary,兼容 WithContext 方法
|
||||
Context = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 工具方法 (Utility Methods) ---
|
||||
|
||||
/// <summary>
|
||||
/// 链式添加上下文信息(Builder 模式)
|
||||
/// 业务用途:在抛出异常前,逐步追加案发现场信息
|
||||
/// </summary>
|
||||
/// <param name="key">上下文键(如 "DeviceIp", "ChannelIndex")</param>
|
||||
/// <param name="value">上下文值</param>
|
||||
/// <returns>当前异常实例(支持链式调用)</returns>
|
||||
public CameraException WithContext(string key, object value)
|
||||
{
|
||||
// 强制转换为可写的 Dictionary,保证上下文可追加
|
||||
if (Context is Dictionary<string, object> contextDict)
|
||||
{
|
||||
contextDict[key] = value;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写 ToString 方法,输出标准化异常日志
|
||||
/// 格式:[CameraError] Brand: {品牌} | Code: {标准码}({原始码}) | Message: {描述} | Context: {上下文}
|
||||
/// </summary>
|
||||
/// <returns>格式化的异常字符串</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var contextStr = Context.Count > 0
|
||||
? $" | Context: {string.Join(", ", Context.Select(kvp => $"{kvp.Key}={kvp.Value}"))}"
|
||||
: string.Empty;
|
||||
|
||||
return $"[CameraError] Brand: {Brand} | Code: {ErrorCode}({RawErrorCode}) | Message: {Message}{contextStr}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
55
SHH.CameraSdk/Abstractions/Errors/RecoveryAction.cs
Normal file
55
SHH.CameraSdk/Abstractions/Errors/RecoveryAction.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// 故障恢复决策建议枚举
|
||||
/// 核心职责:定义标准化的故障自愈动作指令,指导 <see cref="RecoveryPolicy"/> 与 <see cref="BaseVideoSource"/> 执行差异化恢复逻辑
|
||||
/// 设计原则:按“无动作→自动恢复→降级→致命停止→人工介入”的优先级划分,覆盖全场景故障处理
|
||||
/// </summary>
|
||||
public enum RecoveryAction
|
||||
{
|
||||
#region --- 0. 基础状态 ---
|
||||
|
||||
/// <summary>
|
||||
/// 正常状态,无需执行任何恢复动作
|
||||
/// 适用场景:错误码为 Success、设备运行正常
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 1. 自动恢复动作 ---
|
||||
|
||||
/// <summary>
|
||||
/// 自动指数退避重试
|
||||
/// 适用场景:网络抖动、超时、设备资源繁忙等**暂时性故障**
|
||||
/// 执行标准:采用 2^n * 1000ms 算法计算延迟,上限 2 分钟,避免频繁重试加剧系统负载
|
||||
/// </summary>
|
||||
RetryWithBackoff,
|
||||
|
||||
/// <summary>
|
||||
/// 降级运行
|
||||
/// 适用场景:主码流超限、高清分辨率不支持等**非致命功能降级场景**
|
||||
/// 执行标准:自动切换到备用方案(如主码流→子码流、4K→1080P),保证基础功能可用
|
||||
/// </summary>
|
||||
Degrade,
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2. 终止与人工动作 ---
|
||||
|
||||
/// <summary>
|
||||
/// 致命停止,禁止继续重试
|
||||
/// 适用场景:密码错误、账号锁定、IP 拉黑等**不可自愈的认证/权限类故障**
|
||||
/// 执行标准:立即停止自愈引擎,推送告警信息到运维平台,记录详细错误日志
|
||||
/// </summary>
|
||||
FatalStop,
|
||||
|
||||
/// <summary>
|
||||
/// 需要人工介入处理
|
||||
/// 适用场景:硬件故障、磁盘满、SDK 组件缺失等**软件无法修复的底层故障**
|
||||
/// 执行标准:触发告警通知,标记设备状态为 Faulted,等待运维人员排查
|
||||
/// </summary>
|
||||
ManualIntervention
|
||||
|
||||
#endregion
|
||||
}
|
||||
100
SHH.CameraSdk/Abstractions/Errors/RecoveryPolicy.cs
Normal file
100
SHH.CameraSdk/Abstractions/Errors/RecoveryPolicy.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// [决策引擎] 故障自愈策略 (V3.3.1 修复版)
|
||||
/// 核心职责:根据设备错误码特征,智能裁决系统应采取的恢复动作,实现故障自动化处理
|
||||
/// 关键修复(Bug R):
|
||||
/// <para>1. 致命错误防护:对 InvalidCredentials/AccountLocked 等错误禁止重试,防止账号被锁、IP 拉黑</para>
|
||||
/// <para>2. 未知错误保守策略:对 Unknown 错误采用 ManualIntervention,避免未知风险扩散</para>
|
||||
/// 设计原则:最小化风险、最大化自愈率,区分可重试/不可重试/需人工干预的错误类型
|
||||
/// </summary>
|
||||
public static class RecoveryPolicy
|
||||
{
|
||||
#region --- 1. 核心决策逻辑:错误码→自愈动作映射 ---
|
||||
|
||||
/// <summary>
|
||||
/// 根据相机错误码判定对应的故障自愈动作
|
||||
/// </summary>
|
||||
/// <param name="code">设备上报的错误码</param>
|
||||
/// <returns>标准化的自愈动作指令</returns>
|
||||
public static RecoveryAction GetAction(CameraErrorCode code)
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
// ========== 场景 A: 网络类故障 (可自愈) ==========
|
||||
// 策略:指数退避重试
|
||||
// 理由:网络波动、超时、闪断为暂时性故障,延迟重试大概率恢复
|
||||
CameraErrorCode.NetworkUnreachable or
|
||||
CameraErrorCode.NetworkSendError or
|
||||
CameraErrorCode.NetworkRecvError or
|
||||
CameraErrorCode.Timeout or
|
||||
CameraErrorCode.SocketError or
|
||||
CameraErrorCode.StreamInterrupted or
|
||||
CameraErrorCode.DeviceRebooting => RecoveryAction.RetryWithBackoff,
|
||||
|
||||
// ========== 场景 B: 资源繁忙类故障 (可自愈) ==========
|
||||
// 策略:指数退避重试
|
||||
// 理由:设备连接数满、缓冲区溢出,等待资源释放后可恢复
|
||||
CameraErrorCode.DeviceResourceBusy or
|
||||
CameraErrorCode.DeviceBufferOverflow or
|
||||
CameraErrorCode.DeviceBusy or
|
||||
CameraErrorCode.OperationNotFinished or
|
||||
CameraErrorCode.PortPoolExhausted or
|
||||
CameraErrorCode.MaxConnectionsReached or
|
||||
CameraErrorCode.MaxStreamExceeded => RecoveryAction.RetryWithBackoff,
|
||||
|
||||
// ========== 场景 C: 致命错误 (不可自愈,禁止重试) ==========
|
||||
// 策略:立即停止
|
||||
// 理由:密码错误、账号锁定、组件缺失等故障,重试无意义且会加剧风险(账号锁死、日志爆炸)
|
||||
CameraErrorCode.InvalidCredentials or
|
||||
CameraErrorCode.AccessDenied or
|
||||
CameraErrorCode.UserNotExist or
|
||||
CameraErrorCode.AccountLocked or
|
||||
CameraErrorCode.SessionExpired or
|
||||
CameraErrorCode.InvalidChannel or
|
||||
CameraErrorCode.IpConflict or
|
||||
CameraErrorCode.SdkNotInitialized or
|
||||
CameraErrorCode.ComponentLoadFailed or
|
||||
CameraErrorCode.EncryptionLibError => RecoveryAction.FatalStop,
|
||||
|
||||
// ========== 场景 D: 硬件故障 (需人工干预) ==========
|
||||
// 策略:人工介入
|
||||
// 理由:硬盘损坏、存储满等故障属于硬件层面,软件无法修复
|
||||
CameraErrorCode.HardwareFault or
|
||||
CameraErrorCode.StorageError or
|
||||
CameraErrorCode.DiskFull or
|
||||
CameraErrorCode.DiskReadOnly => RecoveryAction.ManualIntervention,
|
||||
|
||||
// ========== 场景 E: 正常状态 ==========
|
||||
CameraErrorCode.Success => RecoveryAction.None,
|
||||
|
||||
// ========== 场景 F: 未知错误 (关键修复 Bug R) ==========
|
||||
// 旧策略:盲目重试 → 新策略:人工干预
|
||||
// 理由:未知错误可能包含 IP 拉黑、协议不兼容等严重问题,重试会扩大风险
|
||||
_ => RecoveryAction.ManualIntervention
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2. 辅助算法:指数退避延迟计算 ---
|
||||
|
||||
/// <summary>
|
||||
/// 获取建议的指数退避延迟时间(毫秒)
|
||||
/// 算法公式:delay = min(2^n * 1000, 120000),n = 当前重试次数
|
||||
/// 限流规则:第一次 2s → 第二次 4s → ... → 第六次 64s → 上限 120s(2分钟)
|
||||
/// </summary>
|
||||
/// <param name="retryCount">当前重试次数(从 1 开始计数)</param>
|
||||
/// <returns>延迟毫秒数</returns>
|
||||
public static int GetRetryDelay(int retryCount)
|
||||
{
|
||||
// 限制重试次数最大为 7,防止指数爆炸导致数值溢出
|
||||
int exponent = Math.Min(retryCount, 7);
|
||||
// 计算指数退避秒数
|
||||
int delaySeconds = (int)Math.Pow(2, exponent);
|
||||
// 转换为毫秒并限制上限为 2 分钟(120000ms)
|
||||
return Math.Min(delaySeconds * 1000, 120000);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user