规范并补充日志内容
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
namespace SHH.CameraSdk;
|
||||
using Ayay.SerilogLogs;
|
||||
using Serilog;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// [管理层] 视频源总控管理器 (V3.5 持久化集成版)
|
||||
@@ -8,6 +11,8 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
{
|
||||
#region --- 1. 核心资源与状态 (Fields & States) ---
|
||||
|
||||
private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
|
||||
|
||||
/// <summary> 全局设备实例池(线程安全),Key = 设备唯一标识 </summary>
|
||||
private readonly ConcurrentDictionary<long, BaseVideoSource> _cameraPool = new();
|
||||
|
||||
@@ -26,17 +31,13 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
/// </summary>
|
||||
private volatile bool _isEngineStarted = false;
|
||||
|
||||
// [新增] 存储服务引用
|
||||
private readonly IStorageService _storage;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 构造函数 (Constructor) ---
|
||||
|
||||
// [修改] 注入 IStorageService
|
||||
public CameraManager(IStorageService storage)
|
||||
public CameraManager()
|
||||
{
|
||||
_storage = storage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -59,17 +60,15 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
{
|
||||
// 如果添加失败(ID冲突),由于 device 还没被使用,直接释放掉
|
||||
device.DisposeAsync().AsTask().Wait();
|
||||
|
||||
_sysLog.Warning($"[Core] 设备 ID:{config.Id} 已存在");
|
||||
_sysLog.Debug($"[Core] 设备 ID:{config.Id} => 明细:" + "{@cfg}.", config);
|
||||
throw new InvalidOperationException($"设备 ID {config.Id} 已存在");
|
||||
}
|
||||
|
||||
// 动态激活逻辑:引擎已启动时,新设备直接标记为运行状态
|
||||
if (_isEngineStarted)
|
||||
{
|
||||
device.IsRunning = true;
|
||||
}
|
||||
|
||||
// [新增] 自动保存到文件
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,7 +93,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
if (_cameraPool.TryRemove(id, out var device))
|
||||
{
|
||||
// 记录日志
|
||||
Console.WriteLine($"[Manager] 正在移除设备 {id}...");
|
||||
_sysLog.Information("[Core] 正在移除设备, ID {0} ", id);
|
||||
|
||||
// 1. 停止物理连接
|
||||
await device.StopAsync();
|
||||
@@ -102,10 +101,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
// 2. 释放资源
|
||||
await device.DisposeAsync();
|
||||
|
||||
Console.WriteLine($"[Manager] 设备 {id} 已彻底移除");
|
||||
|
||||
// [新增] 自动保存到文件
|
||||
SaveChanges();
|
||||
_sysLog.Warning("[Core] 设备已彻底移除, ID {0} ", id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,36 +121,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
if (_isEngineStarted) return;
|
||||
|
||||
// =========================================================
|
||||
// 1. [新增] 从文件加载设备配置
|
||||
// =========================================================
|
||||
try
|
||||
{
|
||||
Console.WriteLine("[Manager] 正在检查本地配置文件...");
|
||||
var savedConfigs = await _storage.LoadDevicesAsync();
|
||||
|
||||
int loadedCount = 0;
|
||||
foreach (var config in savedConfigs)
|
||||
{
|
||||
// 防止ID冲突(虽然文件里理论上不重复)
|
||||
if (!_cameraPool.ContainsKey(config.Id))
|
||||
{
|
||||
var device = CreateDeviceInstance(config);
|
||||
// 默认设为运行状态,让协调器稍后去连接
|
||||
//device.IsRunning = true;
|
||||
_cameraPool.TryAdd(config.Id, device);
|
||||
loadedCount++;
|
||||
}
|
||||
}
|
||||
if (loadedCount > 0)
|
||||
Console.WriteLine($"[Manager] 已从文件恢复 {loadedCount} 台设备配置");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Manager] 加载配置文件警告: {ex.Message}");
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 2. 全局驱动环境预初始化
|
||||
// 1. 全局驱动环境预初始化
|
||||
// =========================================================
|
||||
HikSdkManager.Initialize();
|
||||
|
||||
@@ -162,7 +129,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
_isEngineStarted = true;
|
||||
|
||||
// =========================================================
|
||||
// 3. 启动协调器后台自愈循环
|
||||
// 2. 启动协调器后台自愈循环
|
||||
// =========================================================
|
||||
_ = Task.Factory.StartNew(
|
||||
() => _coordinator.RunCoordinationLoopAsync(_globalCts.Token),
|
||||
@@ -175,7 +142,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
// *注意*:如果 Coordinator 需要显式注册,请在这里补上:
|
||||
foreach (var dev in _cameraPool.Values) _coordinator.Register(dev);
|
||||
|
||||
Console.WriteLine($"[CameraManager] 引擎启动成功,当前管理 {_cameraPool.Count} 路相机设备。");
|
||||
_sysLog.Warning($"[Core] 设备管理引擎启动成功, 当前管理 {_cameraPool.Count} 路设备");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -191,20 +158,6 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
|
||||
#region --- 4. 监控数据采集 (Telemetry Collection) ---
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有相机的健康度报告
|
||||
/// </summary>
|
||||
public IEnumerable<CameraHealthReport> GetDetailedTelemetry()
|
||||
{
|
||||
return _cameraPool.Values.Select(cam => new CameraHealthReport
|
||||
{
|
||||
DeviceId = cam.Id,
|
||||
Ip = cam.Config.IpAddress,
|
||||
Status = cam.Status.ToString(),
|
||||
LastError = cam.Status == VideoSourceStatus.Faulted ? "设备故障或网络中断" : "运行正常"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取全量相机实时遥测数据快照 (MonitorController 使用)
|
||||
/// </summary>
|
||||
@@ -250,10 +203,13 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
public async Task UpdateDeviceConfigAsync(long deviceId, DeviceUpdateDto dto)
|
||||
{
|
||||
if (!_cameraPool.TryGetValue(deviceId, out var device))
|
||||
throw new KeyNotFoundException($"设备 {deviceId} 不存在");
|
||||
{
|
||||
_sysLog.Warning($"[Core] 设备更新制作, ID:{deviceId} 不存在.");
|
||||
throw new KeyNotFoundException($"设备 ID:{deviceId} 不存在.");
|
||||
}
|
||||
|
||||
// 1. 审计
|
||||
device.AddAuditLog("收到配置更新请求");
|
||||
_sysLog.Debug($"[Core] 响应设备配置更新请求, ID:{deviceId}.");
|
||||
|
||||
// 2. 创建副本进行对比
|
||||
var oldConfig = device.Config;
|
||||
@@ -287,7 +243,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
|
||||
if (needColdRestart)
|
||||
{
|
||||
device.AddAuditLog($"检测到核心参数变更,执行冷重启 (Reboot)");
|
||||
_sysLog.Debug($"[Core] 检测到核心参数变更, 执行冷重启, ID:{deviceId}.");
|
||||
bool wasRunning = device.IsRunning;
|
||||
|
||||
// A. 彻底停止
|
||||
@@ -301,7 +257,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
device.AddAuditLog($"检测到运行时参数变更,执行热更新 (HotSwap)");
|
||||
_sysLog.Debug($"[Core] 检测到运行时参数变更, 执行热更新, ID:{deviceId}.");
|
||||
|
||||
// A. 更新配置数据
|
||||
device.UpdateConfig(newConfig);
|
||||
@@ -317,47 +273,6 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
device.ApplyOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
// [新增] 保存文件
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全量替换更新 (兼容接口)
|
||||
/// </summary>
|
||||
public async Task UpdateDeviceAsync(int id, VideoSourceConfig newConfig)
|
||||
{
|
||||
if (!_cameraPool.TryGetValue(id, out var oldDevice))
|
||||
throw new KeyNotFoundException($"设备 #{id} 不存在");
|
||||
|
||||
bool wasRunning = oldDevice.IsRunning ||
|
||||
oldDevice.Status == VideoSourceStatus.Playing ||
|
||||
oldDevice.Status == VideoSourceStatus.Connecting;
|
||||
|
||||
Console.WriteLine($"[Manager] 正在更新设备 #{id},配置变更中...");
|
||||
|
||||
try
|
||||
{
|
||||
await oldDevice.StopAsync();
|
||||
await oldDevice.DisposeAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Manager] 销毁旧设备时警告: {ex.Message}");
|
||||
}
|
||||
|
||||
var newDevice = CreateDeviceInstance(newConfig);
|
||||
_cameraPool[id] = newDevice;
|
||||
|
||||
Console.WriteLine($"[Manager] 设备 #{id} 实例已重建。");
|
||||
|
||||
if (wasRunning)
|
||||
{
|
||||
await newDevice.StartAsync();
|
||||
}
|
||||
|
||||
// [新增] 保存文件
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -366,6 +281,10 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
|
||||
public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
@@ -402,31 +321,38 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
|
||||
#region --- 7. 内部辅助 (Helpers) ---
|
||||
|
||||
/// <summary>
|
||||
/// 创建设备实例
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
private BaseVideoSource CreateDeviceInstance(VideoSourceConfig config)
|
||||
{
|
||||
return config.Brand switch
|
||||
{
|
||||
DeviceBrand.HikVision => new HikVideoSource(config),
|
||||
_ => throw new NotSupportedException($"不支持的设备品牌: {config.Brand}")
|
||||
|
||||
// 使用模式匹配获取不匹配的值,记录详细的 DTO 上下文
|
||||
_ => HandleUnsupportedBrand(config)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [新增] 触发异步保存 (Fire-and-Forget)
|
||||
/// 不阻塞当前 API 线程,让后台存储服务去排队写入
|
||||
/// 处理不支持的设备品牌
|
||||
/// </summary>
|
||||
private void SaveChanges()
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
private BaseVideoSource HandleUnsupportedBrand(VideoSourceConfig config)
|
||||
{
|
||||
try
|
||||
{
|
||||
var allConfigs = _cameraPool.Values.Select(d => d.Config).ToList();
|
||||
// 异步调用存储服务,不使用 await 以免阻塞 API 响应
|
||||
_ = _storage.SaveDevicesAsync(allConfigs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[Manager] 触发保存失败: {ex.Message}");
|
||||
}
|
||||
// 1. 构造错误消息
|
||||
string errorMsg = $"❌ 不支持的设备品牌: {config.Brand} (ID: {config.Id}, Name: {config.Name})";
|
||||
|
||||
// 2. 写入日志 - 建议带上 config 的解构信息,方便排查是否是前端传参错误
|
||||
_sysLog.Error($"[Core] {errorMsg} | 配置详情: " + "{@Config}", config);
|
||||
|
||||
// 3. 抛出异常,阻止程序进入不确定状态
|
||||
throw new NotSupportedException(errorMsg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user