Files
OrpaonVision/OrpaonVision.SiteApp/Runtime/Services/MockHikCameraService.cs
2026-04-06 22:04:05 +08:00

448 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// 海康相机服务模拟实现。
/// </summary>
public sealed class MockHikCameraService : IHikCameraService, IDisposable
{
private readonly ILogger<MockHikCameraService> _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<byte[]> _frameQueue = new();
private int _frameCounter;
/// <summary>
/// 构造函数。
/// </summary>
public MockHikCameraService(ILogger<MockHikCameraService> logger, IOptions<HikCameraOptions> options)
{
_logger = logger;
_options = options.Value;
_logger.LogInformation("海康相机服务已初始化(模拟模式)");
}
/// <inheritdoc />
public Result<IReadOnlyList<HikCameraDevice>> EnumerateDevices()
{
try
{
_logger.LogInformation("正在枚举海康相机设备(模拟模式)...");
// 模拟设备枚举
var devices = new List<HikCameraDevice>
{
new()
{
SerialNumber = "MOCK_HK_001",
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 = "MOCK_HK_002",
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<IReadOnlyList<HikCameraDevice>>.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<IReadOnlyList<HikCameraDevice>>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, [.. result.Errors]);
}
}
/// <inheritdoc />
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);
// 模拟连接过程
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());
}
}
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public Result Disconnect()
{
lock (_lockObject)
{
try
{
if (!_isConnected)
{
return Result.Success(message: "相机未连接。");
}
_logger.LogInformation("正在断开相机连接(模拟)...");
// 停止采集
if (_isGrabbing)
{
StopGrabbing();
}
// 模拟断开过程
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());
}
}
}
/// <inheritdoc />
public bool IsConnected => _isConnected;
/// <inheritdoc />
public HikCameraDevice? CurrentDevice => _currentDevice;
/// <inheritdoc />
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);
// 模拟参数设置
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());
}
}
}
/// <inheritdoc />
public Result StartGrabbing()
{
lock (_lockObject)
{
try
{
if (!_isConnected)
{
return Result.Fail("HIK_CAMERA_NOT_CONNECTED", "相机未连接。");
}
if (_isGrabbing)
{
return Result.Success(message: "相机已在采集中。");
}
_logger.LogInformation("正在开始图像采集(模拟)...");
// 模拟开始采集
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());
}
}
}
/// <inheritdoc />
public Result StopGrabbing()
{
lock (_lockObject)
{
try
{
if (!_isGrabbing)
{
return Result.Success(message: "相机未在采集状态。");
}
_logger.LogInformation("正在停止图像采集(模拟)...");
// 停止模拟定时器
_simulationTimer?.Dispose();
_simulationTimer = null;
// 模拟停止采集
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());
}
}
}
/// <inheritdoc />
public Result TriggerSoftware()
{
lock (_lockObject)
{
try
{
if (!_isConnected)
{
return Result.Fail("HIK_CAMERA_NOT_CONNECTED", "相机未连接。");
}
if (!_isGrabbing)
{
return Result.Fail("HIK_CAMERA_NOT_GRABBING", "相机未在采集状态。");
}
// 模拟软触发
_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());
}
}
}
/// <inheritdoc />
public Result<byte[]> GetLatestFrame()
{
try
{
if (!_isGrabbing)
{
return Result<byte[]>.Fail("HIK_CAMERA_NOT_GRABBING", "相机未在采集状态。");
}
if (_frameQueue.TryDequeue(out var frame))
{
return Result<byte[]>.Success(frame, message: "获取图像帧成功(模拟)。");
}
return Result<byte[]>.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<byte[]>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public Result<CameraFrameDto> GetLatestFrameWithTimestamp()
{
var frameResult = GetLatestFrame();
if (!frameResult.Succeeded)
{
return Result<CameraFrameDto>.Fail(frameResult.Code, frameResult.Message, frameResult.Errors.ToArray());
}
return Result<CameraFrameDto>.Success(new CameraFrameDto
{
Timestamp = DateTime.UtcNow,
ImageData = frameResult.Data,
Width = _options.ImageWidth,
Height = _options.ImageHeight,
PixelFormat = _options.PixelFormat
}, message: "获取图像帧成功(模拟)。");
}
/// <inheritdoc />
public event Action<byte[], DateTime>? OnImageReceived;
/// <inheritdoc />
public event Action<bool, string>? OnConnectionStateChanged;
/// <summary>
/// 生成模拟图像帧。
/// </summary>
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, "生成模拟图像帧失败");
}
}
/// <summary>
/// 释放资源。
/// </summary>
public void Dispose()
{
try
{
Disconnect();
_simulationTimer?.Dispose();
}
catch (Exception ex)
{
_logger.LogError(ex, "释放海康相机服务资源时发生异常");
}
}
}