增加云台移动、缩放、聚集、光圈、校时、重启对 AiVideo 项目的支持

This commit is contained in:
2026-03-02 13:57:10 +08:00
parent c8f25aeba5
commit 0399871467
11 changed files with 343 additions and 28 deletions

View File

@@ -32,7 +32,7 @@ public static class Bootstrapper
// 视频流地址 (格式: IP,Port,Type,Desc)
"--uris", "localhost,9001,调试PC;",
"--uris", "localhost,9002,调试PC;",
//"--uris", "localhost,9002,调试PC;",
// 日志中心配置 (格式: IP,Port,Desc)
"--sequris", "58.216.225.5,20026,日志处置中心;",

View File

@@ -0,0 +1,69 @@
using Ayay.SerilogLogs;
using Newtonsoft.Json.Linq;
using Serilog;
using SHH.CameraSdk;
using SHH.Contracts;
namespace SHH.CameraService;
/// <summary>
/// 设备重启指令处理器
/// 响应 gRpc 指令ProtocolCodes.Device_Reboot
/// </summary>
public class DeviceRebootHandler : ICommandHandler
{
private readonly ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
private readonly CameraManager _cameraManager;
/// <summary>指令名称(需在 ProtocolCodes 中定义 Device_Reboot</summary>
public string ActionName => ProtocolCodes.Device_Reboot;
public DeviceRebootHandler(CameraManager cameraManager)
{
_cameraManager = cameraManager ?? throw new ArgumentNullException(nameof(cameraManager));
}
public async Task ExecuteAsync(JToken payload)
{
// 1. 解析参数(假设重启指令至少包含 DeviceId
// Optimized: 使用通用的基础 DTO 或直接解析 DeviceId避免为简单的重启创建过多复杂 DTO
var deviceId = payload["DeviceId"]?.Value<int>() ?? 0;
if (deviceId <= 0)
{
_sysLog.Warning("[Reboot] 无效指令设备ID非法");
return;
}
// 2. 获取设备实例
var device = _cameraManager.GetDevice(deviceId);
if (device == null)
{
_sysLog.Warning($"[Reboot] 设备 {deviceId} 不存在");
return;
}
// 3. 校验重启能力 (参考 PtzControlHandler 模式)
// Modified: 根据 ISyncFeature.cs 定义,检查是否实现了 IRebootFeature 接口
if (!(device is IRebootFeature rebootFeature))
{
_sysLog.Warning($"[Reboot] 设备 {deviceId} 不支持远程重启接口");
return;
}
// 4. 执行重启
try
{
_sysLog.Information($"[Reboot] 正在向设备 {deviceId} 发送重启指令...");
// 调用接口定义的异步方法
await rebootFeature.RebootAsync();
_sysLog.Information($"[Reboot] 设备 {deviceId} 重启指令发送成功");
}
catch (Exception ex)
{
_sysLog.Error($"[Reboot] 设备 {deviceId} 重启失败: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,77 @@
using Ayay.SerilogLogs;
using Newtonsoft.Json.Linq;
using Serilog;
using SHH.CameraSdk;
using SHH.Contracts;
namespace SHH.CameraService;
/// <summary>
/// 云台控制指令处理器
/// 响应 gRpc 指令ProtocolCodes.Control_Ptz
/// </summary>
public class PtzControlHandler : ICommandHandler
{
private readonly ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
private readonly CameraManager _cameraManager;
/// <summary>指令名称(需与网关下发的 CmdCode 一致)</summary>
public string ActionName => ProtocolCodes.Ptz_Control; // 需在 ProtocolCodes 中新增该常量
public PtzControlHandler(CameraManager cameraManager)
{
_cameraManager = cameraManager ?? throw new ArgumentNullException(nameof(cameraManager));
}
public async Task ExecuteAsync(JToken payload)
{
// 1. 解析 PTZ 控制参数(与网关下发的 JSON 结构匹配)
var ptzDto = payload.ToObject<PtzControlDto>();
if (ptzDto == null || ptzDto.DeviceId <= 0)
{
_sysLog.Warning("[PTZ] 无效指令参数缺失或设备ID非法");
return;
}
// 2. 获取目标设备并校验能力
var device = _cameraManager.GetDevice(ptzDto.DeviceId);
if (device == null)
{
_sysLog.Warning($"[PTZ] 设备 {ptzDto.DeviceId} 不存在");
return;
}
if (!device.IsPhysicalOnline)
{
_sysLog.Warning($"[PTZ] 设备 {ptzDto.DeviceId} 未在线,无法执行云台控制");
return;
}
if (!(device is IPtzFeature ptzFeature))
{
_sysLog.Warning($"[PTZ] 设备 {ptzDto.DeviceId} 不支持云台控制");
return;
}
// 3. 执行云台控制(根据指令类型选择手动/点动模式)
try
{
if (ptzDto.Duration > 0)
{
// 点动模式自动复位如持续300ms向上转动
int safeDuration = Math.Clamp(ptzDto.Duration, 50, 2000); // 限制单次最长2秒
await ptzFeature.PtzStepAsync(ptzDto.Action, safeDuration, ptzDto.Speed);
_sysLog.Information($"[PTZ] 设备 {ptzDto.DeviceId} 点动控制:{ptzDto.Action},时长 {safeDuration}ms速度 {ptzDto.Speed}");
}
else
{
// 手动模式:按下/松开(如按住向上、松开停止)
await ptzFeature.PtzControlAsync(ptzDto.Action, ptzDto.Stop, ptzDto.Speed);
string actionDesc = ptzDto.Stop ? "停止" : "启动";
_sysLog.Information($"[PTZ] 设备 {ptzDto.DeviceId} 手动控制:{ptzDto.Action} {actionDesc},速度 {ptzDto.Speed}");
}
}
catch (Exception ex)
{
_sysLog.Error(ex, $"[PTZ] 设备 {ptzDto.DeviceId} 控制失败");
}
}
}

View File

@@ -0,0 +1,79 @@
using Ayay.SerilogLogs;
using Newtonsoft.Json.Linq;
using Serilog;
using SHH.CameraSdk;
using SHH.Contracts;
namespace SHH.CameraService;
/// <summary>
/// 设备时间同步处理器
/// 响应 gRpc 指令ProtocolCodes.Time_Sync
/// </summary>
public class TimeSyncHandler : ICommandHandler
{
private readonly ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
private readonly CameraManager _cameraManager;
// Optimized: 需在 ProtocolCodes 中定义此常量,确保与网关下发的 CmdCode 对应
public string ActionName => ProtocolCodes.Device_TimeSync;
public TimeSyncHandler(CameraManager cameraManager)
{
_cameraManager = cameraManager ?? throw new ArgumentNullException(nameof(cameraManager));
}
public async Task ExecuteAsync(JToken payload)
{
// 1. 解析基础参数
var deviceId = payload["DeviceId"]?.Value<int>() ?? 0;
var method = payload["Method"]?.Value<string>()?.ToLower(); // "get" 或 "set"
if (deviceId <= 0)
{
_sysLog.Warning("[TimeSync] 无效指令设备ID非法");
return;
}
// 2. 获取设备并校验能力
var device = _cameraManager.GetDevice(deviceId);
if (device == null)
{
_sysLog.Warning($"[TimeSync] 设备 {deviceId} 不存在");
return;
}
// Modified: 根据 ISyncFeature.cs 校验是否实现 ITimeSyncFeature 接口
if (!(device is ITimeSyncFeature syncFeature))
{
_sysLog.Warning($"[TimeSync] 设备 {deviceId} 不支持时间同步功能");
return;
}
// 3. 业务逻辑分支
try
{
if (method == "set")
{
// 解析待设置的时间,若无则默认使用服务器当前系统时间
var targetTime = payload["SyncTime"]?.Value<DateTime>() ?? DateTime.Now;
// 调用接口设置时间
await syncFeature.SetTimeAsync(targetTime);
_sysLog.Information($"[TimeSync] 设备 {deviceId} 时间已同步为: {targetTime:yyyy-MM-dd HH:mm:ss}");
}
else
{
// 执行获取时间
var deviceTime = await syncFeature.GetTimeAsync();
_sysLog.Information($"[TimeSync] 获取设备 {deviceId} 当前时间成功: {deviceTime:yyyy-MM-dd HH:mm:ss}");
// TODO: 若需要将结果返回给网关,需在此处调用 GatewayService 异步回传
}
}
catch (Exception ex)
{
_sysLog.Error(ex, $"[TimeSync] 设备 {deviceId} 操作失败 ({method})");
}
}
}

View File

@@ -59,6 +59,9 @@ public static class ServiceCollectionExtensions
services.AddSingleton<CommandDispatcher>();
services.AddSingleton<ICommandHandler, DeviceConfigHandler>();
services.AddSingleton<ICommandHandler, RemoveCameraHandler>();
services.AddSingleton<ICommandHandler, PtzControlHandler>();
services.AddSingleton<ICommandHandler, DeviceRebootHandler>();
services.AddSingleton<ICommandHandler, TimeSyncHandler>();
}
#endregion