using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Contracts; using OrpaonVision.SiteApp.Runtime.Services; using System.Collections.Concurrent; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 海康相机服务实现。 /// 注意:这是一个模拟实现,实际使用时需要替换为海康相机SDK的真实调用。 /// public sealed class HikCameraService : IHikCameraService, IDisposable { private readonly ILogger _logger; private readonly HikCameraOptions _options; private readonly object _lockObject = new(); private HikCameraDevice? _currentDevice; private bool _isConnected; private bool _isGrabbing; private Timer? _simulationTimer; private readonly ConcurrentQueue _frameQueue = new(); private int _frameCounter; /// /// 构造函数。 /// public HikCameraService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; _logger.LogInformation("海康相机服务已初始化(模拟模式)"); } /// public Result> EnumerateDevices() { try { _logger.LogInformation("正在枚举海康相机设备..."); // 模拟设备枚举 - 实际实现需要调用海康SDK的MV_CC_EnumDevices等函数 var devices = new List { new() { SerialNumber = "HK0012345678", DeviceName = "模拟海康相机1", IpAddress = "192.168.1.100", SubnetMask = "255.255.255.0", DefaultGateway = "192.168.1.1", MacAddress = "00:11:22:33:44:55", FirmwareVersion = "V2.1.0", IsConnected = false }, new() { SerialNumber = "HK0087654321", DeviceName = "模拟海康相机2", IpAddress = "192.168.1.101", SubnetMask = "255.255.255.0", DefaultGateway = "192.168.1.1", MacAddress = "00:11:22:33:44:66", FirmwareVersion = "V2.1.0", IsConnected = false } }; _logger.LogInformation("发现 {Count} 个海康相机设备", devices.Count); return Result>.Success(devices, message: "设备枚举成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "枚举海康相机设备失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_ENUM_FAILED", "枚举相机设备失败。", traceId); return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, [.. result.Errors]); } } /// public Result ConnectToDevice(HikCameraDevice device) { if (device == null) { return Result.Fail("HIK_CAMERA_DEVICE_NULL", "相机设备不能为空。"); } lock (_lockObject) { try { if (_isConnected) { return Result.Fail("HIK_CAMERA_ALREADY_CONNECTED", "相机已连接,请先断开。"); } _logger.LogInformation("正在连接相机设备: {SerialNumber}", device.SerialNumber); // 模拟连接过程 - 实际实现需要调用海康SDK的MV_CC_CreateHandle、MV_CC_OpenDevice等函数 Thread.Sleep(500); // 模拟连接延迟 _currentDevice = new HikCameraDevice { SerialNumber = device.SerialNumber, DeviceName = device.DeviceName, IpAddress = device.IpAddress, SubnetMask = device.SubnetMask, DefaultGateway = device.DefaultGateway, MacAddress = device.MacAddress, FirmwareVersion = device.FirmwareVersion, IsConnected = true, ConnectedAt = DateTime.UtcNow }; _isConnected = true; _logger.LogInformation("相机连接成功: {SerialNumber}", device.SerialNumber); OnConnectionStateChanged?.Invoke(true, $"已连接到 {device.DeviceName}"); return Result.Success(message: $"相机 {device.DeviceName} 连接成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "连接相机失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_CONNECT_FAILED", "连接相机失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result ConnectToDevice(string serialNumberOrIp) { var devicesResult = EnumerateDevices(); if (!devicesResult.Succeeded) { return Result.Fail(devicesResult.Code, devicesResult.Message, devicesResult.Errors.ToArray()); } var targetDevice = devicesResult.Data.FirstOrDefault(d => d.SerialNumber.Equals(serialNumberOrIp, StringComparison.OrdinalIgnoreCase) || d.IpAddress.Equals(serialNumberOrIp, StringComparison.OrdinalIgnoreCase)); if (targetDevice == null) { return Result.Fail("HIK_CAMERA_NOT_FOUND", $"未找到序列号或IP为 {serialNumberOrIp} 的相机设备。"); } return ConnectToDevice(targetDevice); } /// public Result Disconnect() { lock (_lockObject) { try { if (!_isConnected) { return Result.Success(message: "相机未连接。"); } _logger.LogInformation("正在断开相机连接..."); // 停止采集 if (_isGrabbing) { StopGrabbing(); } // 模拟断开过程 - 实际实现需要调用海康SDK的MV_CC_CloseDevice、MV_CC_DestroyHandle等函数 Thread.Sleep(100); var deviceName = _currentDevice?.DeviceName ?? "未知设备"; _currentDevice = null; _isConnected = false; _logger.LogInformation("相机断开连接成功"); OnConnectionStateChanged?.Invoke(false, $"已断开与 {deviceName} 的连接"); return Result.Success(message: "相机断开连接成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "断开相机连接失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_DISCONNECT_FAILED", "断开相机连接失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public bool IsConnected => _isConnected; /// public HikCameraDevice? CurrentDevice => _currentDevice; /// public Result SetCameraParameters(int width, int height, string pixelFormat, int triggerMode = 0) { lock (_lockObject) { try { if (!_isConnected) { return Result.Fail("HIK_CAMERA_NOT_CONNECTED", "相机未连接。"); } _logger.LogInformation("正在设置相机参数: {Width}x{Height}, {PixelFormat}, TriggerMode: {TriggerMode}", width, height, pixelFormat, triggerMode); // 模拟参数设置 - 实际实现需要调用海康SDK的MV_CC_SetIntValue、MV_CC_SetEnumValue等函数 Thread.Sleep(100); _logger.LogInformation("相机参数设置成功"); return Result.Success(message: "相机参数设置成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "设置相机参数失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_SET_PARAMS_FAILED", "设置相机参数失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result StartGrabbing() { lock (_lockObject) { try { if (!_isConnected) { return Result.Fail("HIK_CAMERA_NOT_CONNECTED", "相机未连接。"); } if (_isGrabbing) { return Result.Success(message: "相机已在采集中。"); } _logger.LogInformation("正在开始图像采集..."); // 模拟开始采集 - 实际实现需要调用海康SDK的MV_CC_StartGrabbing函数 Thread.Sleep(100); _isGrabbing = true; _frameQueue.Clear(); // 启动模拟定时器生成图像帧 _simulationTimer = new Timer(GenerateSimulationFrame, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(33)); // 约30fps _logger.LogInformation("图像采集已启动"); return Result.Success(message: "图像采集启动成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "启动图像采集失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_START_GRAB_FAILED", "启动图像采集失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result StopGrabbing() { lock (_lockObject) { try { if (!_isGrabbing) { return Result.Success(message: "相机未在采集状态。"); } _logger.LogInformation("正在停止图像采集..."); // 停止模拟定时器 _simulationTimer?.Dispose(); _simulationTimer = null; // 模拟停止采集 - 实际实现需要调用海康SDK的MV_CC_StopGrabbing函数 Thread.Sleep(50); _isGrabbing = false; _frameQueue.Clear(); _logger.LogInformation("图像采集已停止"); return Result.Success(message: "图像采集停止成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "停止图像采集失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_STOP_GRAB_FAILED", "停止图像采集失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result TriggerSoftware() { lock (_lockObject) { try { if (!_isConnected) { return Result.Fail("HIK_CAMERA_NOT_CONNECTED", "相机未连接。"); } if (!_isGrabbing) { return Result.Fail("HIK_CAMERA_NOT_GRABBING", "相机未在采集状态。"); } // 模拟软触发 - 实际实现需要调用海康SDK的MV_CC_SetCommandValue("TriggerSoftware")函数 _logger.LogDebug("执行软件触发"); return Result.Success(message: "软件触发执行成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "软件触发失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_TRIGGER_FAILED", "软件触发失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } } /// public Result GetLatestFrame() { try { if (!_isGrabbing) { return Result.Fail("HIK_CAMERA_NOT_GRABBING", "相机未在采集状态。"); } if (_frameQueue.TryDequeue(out var frame)) { return Result.Success(frame, message: "获取图像帧成功。"); } return Result.Fail("HIK_CAMERA_NO_FRAME", "暂无可用图像帧。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取图像帧失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "HIK_CAMERA_GET_FRAME_FAILED", "获取图像帧失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result GetLatestFrameWithTimestamp() { var frameResult = GetLatestFrame(); if (!frameResult.Succeeded) { return Result.Fail(frameResult.Code, frameResult.Message, frameResult.Errors.ToArray()); } return Result.Success(new CameraFrameDto { Timestamp = DateTime.UtcNow, ImageData = frameResult.Data, Width = _options.ImageWidth, Height = _options.ImageHeight, PixelFormat = _options.PixelFormat }, message: "获取图像帧成功。"); } /// public event Action? OnImageReceived; /// public event Action? OnConnectionStateChanged; /// /// 生成模拟图像帧。 /// private void GenerateSimulationFrame(object? state) { try { // 生成模拟图像数据(BGR格式) var width = _options.ImageWidth; var height = _options.ImageHeight; var frameSize = width * height * 3; // BGR8Packed var frameData = new byte[frameSize]; // 填充模拟图像数据(简单的渐变图案) var random = new Random(_frameCounter++); for (int i = 0; i < frameSize; i += 3) { frameData[i] = (byte)(i % 256); // B frameData[i + 1] = (byte)((i / 3) % 256); // G frameData[i + 2] = (byte)random.Next(256); // R } var timestamp = DateTime.UtcNow; // 添加到队列 if (_frameQueue.Count < 10) // 限制队列大小防止内存溢出 { _frameQueue.Enqueue(frameData); } // 触发事件 OnImageReceived?.Invoke(frameData, timestamp); } catch (Exception ex) { _logger.LogError(ex, "生成模拟图像帧失败"); } } /// /// 释放资源。 /// public void Dispose() { try { Disconnect(); _simulationTimer?.Dispose(); } catch (Exception ex) { _logger.LogError(ex, "释放海康相机服务资源时发生异常"); } } }