规范并补充日志内容

This commit is contained in:
2026-01-16 14:30:42 +08:00
parent 4e0bb33ce2
commit fd6a82eb4e
28 changed files with 325 additions and 537 deletions

View File

@@ -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