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, "释放海康相机服务资源时发生异常");
}
}
}