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

911 lines
34 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;
using System.Runtime.InteropServices;
using MvCamCtrl.NET;
namespace OrpaonVision.SiteApp.Runtime.Services;
/// <summary>
/// 海康相机服务实现(支持真实和模拟模式)。
/// </summary>
public sealed class RealHikCameraService : IHikCameraService, IDisposable
{
private readonly ILogger<RealHikCameraService> _logger;
private readonly HikCameraOptions _options;
private readonly object _lockObject = new();
private HikCameraDevice? _currentDevice;
private bool _isConnected;
private bool _isGrabbing;
private Timer? _frameTimer;
private readonly ConcurrentQueue<byte[]> _frameQueue = new();
private MyCamera? _camera;
private bool _sdkInitialized;
private IntPtr _deviceHandle = IntPtr.Zero;
private MyCamera.MV_CC_DEVICE_INFO? _deviceInfo;
private int _reconnectAttempts = 0;
/// <summary>
/// 构造函数。
/// </summary>
public RealHikCameraService(ILogger<RealHikCameraService> logger, IOptions<HikCameraOptions> options)
{
_logger = logger;
_options = options.Value;
_logger.LogInformation("海康相机服务已初始化(模式: {RunMode}", _options.RunMode);
if (_options.RunMode == "Real")
{
InitializeSdk();
}
}
/// <inheritdoc />
public bool IsConnected => _isConnected;
/// <inheritdoc />
public HikCameraDevice? CurrentDevice => _currentDevice;
/// <inheritdoc />
public event Action<byte[], DateTime>? OnImageReceived;
/// <inheritdoc />
public event Action<bool, string>? OnConnectionStateChanged;
/// <summary>
/// 初始化海康SDK。
/// </summary>
private void InitializeSdk()
{
try
{
if (_sdkInitialized)
{
return;
}
_logger.LogInformation("正在初始化海康SDK...");
// 初始化海康SDK
var result = MyCamera.MV_CC_Initialize_NET();
if (result != MyCamera.MV_OK)
{
_logger.LogError("海康SDK初始化失败错误代码: {ErrorCode}", result);
return;
}
_camera = new MyCamera();
_sdkInitialized = true;
_logger.LogInformation("海康SDK初始化成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "初始化海康SDK失败将回退到模拟模式");
}
}
/// <inheritdoc />
public Result<IReadOnlyList<HikCameraDevice>> EnumerateDevices()
{
try
{
_logger.LogInformation("正在枚举海康相机设备...");
if (_options.RunMode != "Real" || !_sdkInitialized || _camera == null)
{
_logger.LogWarning("真实模式未启用或SDK未初始化返回模拟设备");
return GetMockDevices();
}
// 使用海康SDK枚举设备
var deviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
var result = _camera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref deviceList);
if (result != MyCamera.MV_OK)
{
_logger.LogError("枚举海康设备失败,错误代码: {ErrorCode}", result);
return GetMockDevices();
}
var devices = new List<HikCameraDevice>();
for (uint i = 0; i < deviceList.nDeviceNum; i++)
{
var deviceInfo = deviceList.MvDeviceInfo[i];
var device = new HikCameraDevice
{
SerialNumber = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.chSerialNumber) ?? $"HK_DEVICE_{i}",
DeviceName = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.chUserDefinedName) ?? $"海康相机_{i}",
IpAddress = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp) ?? "Unknown",
MacAddress = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.nCurrentMac) ?? "Unknown",
IsConnected = true
};
devices.Add(device);
_logger.LogInformation("发现海康设备: {DeviceName} ({SerialNumber})", device.DeviceName, device.SerialNumber);
}
if (devices.Count == 0)
{
_logger.LogInformation("未发现海康设备,返回模拟设备");
return GetMockDevices();
}
_logger.LogInformation("成功枚举到 {Count} 个海康设备", devices.Count);
return Result.Success<IReadOnlyList<HikCameraDevice>>(devices);
}
catch (Exception ex)
{
_logger.LogError(ex, "枚举海康设备失败");
return Result.Fail<IReadOnlyList<HikCameraDevice>>("HIK_ENUM_FAILED", "枚举海康设备失败");
}
}
/// <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);
if (_options.RunMode != "Real" || !_sdkInitialized)
{
_logger.LogWarning("真实模式未启用或SDK未初始化使用模拟连接");
return MockConnect(device);
}
// 查找目标设备信息
MyCamera.MV_CC_DEVICE_INFO targetDeviceInfo = default;
bool foundDevice = false;
var deviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
var enumResult = _camera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref deviceList);
if (enumResult == MyCamera.MV_OK)
{
for (uint i = 0; i < deviceList.nDeviceNum; i++)
{
var deviceInfo = deviceList.MvDeviceInfo[i];
var serialNumber = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.chSerialNumber) ?? "";
var ipAddress = Marshal.PtrToStringAnsi(deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp) ?? "";
if (serialNumber.Equals(device.SerialNumber, StringComparison.OrdinalIgnoreCase) ||
ipAddress.Equals(device.IpAddress, StringComparison.OrdinalIgnoreCase))
{
targetDeviceInfo = deviceInfo;
foundDevice = true;
break;
}
}
}
if (!foundDevice)
{
_logger.LogError("未找到指定的相机设备: {SerialNumber}", device.SerialNumber);
return Result.Fail("HIK_DEVICE_NOT_FOUND", $"未找到指定的相机设备: {device.SerialNumber}");
}
// 创建设备句柄
var createResult = _camera.MV_CC_CreateHandle_NET(ref targetDeviceInfo, out _deviceHandle);
if (createResult != MyCamera.MV_OK)
{
_logger.LogError("创建相机句柄失败,错误代码: {ErrorCode}", createResult);
return Result.Fail("HIK_CREATE_HANDLE_FAILED", $"创建相机句柄失败,错误代码: {createResult}");
}
// 打开设备
var openResult = _camera.MV_CC_OpenDevice_NET(_deviceHandle, MyCamera.MV_ACCESS_Exclusive, 0);
if (openResult != MyCamera.MV_OK)
{
_logger.LogError("打开相机设备失败,错误代码: {ErrorCode}", openResult);
_camera.MV_CC_DestroyHandle_NET(_deviceHandle);
_deviceHandle = IntPtr.Zero;
return Result.Fail("HIK_OPEN_DEVICE_FAILED", $"打开相机设备失败,错误代码: {openResult}");
}
// 获取设备信息
_deviceInfo = targetDeviceInfo;
var deviceInfoResult = GetDeviceInfo();
if (!deviceInfoResult.Succeeded)
{
_camera.MV_CC_CloseDevice_NET(_deviceHandle);
_camera.MV_CC_DestroyHandle_NET(_deviceHandle);
_deviceHandle = IntPtr.Zero;
_deviceInfo = null;
return Result.Fail(deviceInfoResult.Code, deviceInfoResult.Message, deviceInfoResult.Errors.ToArray());
}
_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;
_reconnectAttempts = 0;
_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();
}
// 实际实现需要调用海康SDK的MV_CC_CloseDevice、MV_CC_DestroyHandle等函数
if (_options.RunMode == "Real" && _deviceHandle != IntPtr.Zero && _camera != null)
{
// 关闭设备
var closeResult = _camera.MV_CC_CloseDevice_NET(_deviceHandle);
if (closeResult != MyCamera.MV_OK)
{
_logger.LogWarning("关闭相机设备失败,错误代码: {ErrorCode}", closeResult);
}
// 销毁句柄
var destroyResult = _camera.MV_CC_DestroyHandle_NET(_deviceHandle);
if (destroyResult != MyCamera.MV_OK)
{
_logger.LogWarning("销毁相机句柄失败,错误代码: {ErrorCode}", destroyResult);
}
_deviceHandle = IntPtr.Zero;
_deviceInfo = null;
}
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 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);
if (_options.RunMode != "Real" || _deviceHandle == IntPtr.Zero || _camera == null)
{
_logger.LogDebug("模拟模式:设置相机参数");
return Result.Success(message: "相机参数设置成功(模拟模式)。");
}
// 设置图像宽度
var widthResult = _camera.MV_CC_SetIntValue_NET(_deviceHandle, "Width", width);
if (widthResult != MyCamera.MV_OK)
{
_logger.LogWarning("设置图像宽度失败,错误代码: {ErrorCode}", widthResult);
}
// 设置图像高度
var heightResult = _camera.MV_CC_SetIntValue_NET(_deviceHandle, "Height", height);
if (heightResult != MyCamera.MV_OK)
{
_logger.LogWarning("设置图像高度失败,错误代码: {ErrorCode}", heightResult);
}
// 设置像素格式
var pixelFormatResult = _camera.MV_CC_SetEnumValue_NET(_deviceHandle, "PixelFormat", GetPixelFormatValue(pixelFormat));
if (pixelFormatResult != MyCamera.MV_OK)
{
_logger.LogWarning("设置像素格式失败,错误代码: {ErrorCode}", pixelFormatResult);
}
// 设置触发模式
var triggerModeResult = _camera.MV_CC_SetEnumValue_NET(_deviceHandle, "TriggerMode", triggerMode);
if (triggerModeResult != MyCamera.MV_OK)
{
_logger.LogWarning("设置触发模式失败,错误代码: {ErrorCode}", triggerModeResult);
}
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("正在开始图像采集...");
if (_options.RunMode != "Real" || _deviceHandle == IntPtr.Zero || _camera == null)
{
_logger.LogDebug("模拟模式:开始图像采集");
_isGrabbing = true;
_frameQueue.Clear();
// 启动定时器生成模拟帧
_frameTimer = new Timer(GenerateMockFrame, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(33)); // 约30fps
return Result.Success(message: "图像采集启动成功(模拟模式)。");
}
// 开始采集
var startResult = _camera.MV_CC_StartGrabbing_NET(_deviceHandle);
if (startResult != MyCamera.MV_OK)
{
_logger.LogError("启动图像采集失败,错误代码: {ErrorCode}", startResult);
return Result.Fail("HIK_START_GRABBING_FAILED", $"启动图像采集失败,错误代码: {startResult}");
}
Thread.Sleep(100); // 等待采集启动
_isGrabbing = true;
_frameQueue.Clear();
// 启动定时器获取图像帧
_frameTimer = new Timer(GetFrameFromCamera, 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("正在停止图像采集...");
// 停止定时器
_frameTimer?.Dispose();
_frameTimer = null;
if (_options.RunMode == "Real" && _deviceHandle != IntPtr.Zero && _camera != null)
{
var stopResult = _camera.MV_CC_StopGrabbing_NET(_deviceHandle);
if (stopResult != MyCamera.MV_OK)
{
_logger.LogWarning("停止图像采集失败,错误代码: {ErrorCode}", stopResult);
}
}
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", "相机未在采集状态。");
}
if (_options.RunMode == "Real" && _camera != null)
{
_logger.LogDebug("执行软件触发");
var triggerResult = _camera.MV_CC_SetCommandValue_NET(_deviceHandle, "TriggerSoftware");
if (triggerResult != MyCamera.MV_OK)
{
_logger.LogWarning("软件触发失败,错误代码: {ErrorCode}", triggerResult);
return Result.Fail("HIK_CAMERA_TRIGGER_FAILED", $"软件触发失败,错误代码: {triggerResult}");
}
}
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());
}
var now = DateTime.UtcNow;
return Result<CameraFrameDto>.Success(new CameraFrameDto
{
FrameId = Guid.NewGuid(),
CapturedAtUtc = now,
CameraId = _currentDevice?.SerialNumber ?? string.Empty,
ImageData = frameResult.Data,
Timestamp = now,
Width = _options.ImageWidth,
Height = _options.ImageHeight,
PixelFormat = _options.PixelFormat
}, message: "获取图像帧成功。");
}
/// <summary>
/// 从相机获取图像帧。
/// </summary>
private void GetFrameFromCamera(object? state)
{
try
{
if (!_isGrabbing || _options.RunMode != "Real" || !_sdkInitialized)
{
return;
}
if (_deviceHandle == IntPtr.Zero || _camera == null)
{
return;
}
// 获取一帧图像数据
var frame = new MyCamera.MV_FRAME_OUT_INFO_EX();
var imageBuffer = IntPtr.Zero;
var getFrameResult = _camera.MV_CC_GetOneFrameTimeout_NET(_deviceHandle, ref frame, 1000);
if (getFrameResult == MyCamera.MV_OK)
{
try
{
// 转换图像数据为BGR格式
var convertResult = _camera.MV_CC_ConvertPixelType_NET(_deviceHandle, frame, MyCamera.MV_Gvsp_RGB8_Packed, ref imageBuffer);
if (convertResult == MyCamera.MV_OK && imageBuffer != IntPtr.Zero)
{
var frameSize = frame.nWidth * frame.nHeight * 3; // BGR8Packed
var frameData = new byte[frameSize];
Marshal.Copy(imageBuffer, frameData, 0, frameSize);
var timestamp = DateTime.UtcNow;
// 添加到队列
if (_frameQueue.Count < 10) // 限制队列大小防止内存溢出
{
_frameQueue.Enqueue(frameData);
}
// 触发事件
OnImageReceived?.Invoke(frameData, timestamp);
// 释放图像缓冲区
_camera.MV_CC_FreeImageBuffer_NET(_deviceHandle, ref frame);
}
else
{
// 转换失败,使用模拟数据
GenerateMockFrame(null);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "处理图像帧数据时发生异常");
}
finally
{
// 确保释放缓冲区
if (imageBuffer != IntPtr.Zero)
{
_camera.MV_CC_FreeImageBuffer_NET(_deviceHandle, ref frame);
}
}
}
else
{
// 获取图像失败,使用模拟数据
GenerateMockFrame(null);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "从相机获取图像帧失败");
}
}
/// <summary>
/// 生成模拟图像帧。
/// </summary>
private void GenerateMockFrame(object? state)
{
try
{
var width = _options.ImageWidth;
var height = _options.ImageHeight;
var frameSize = width * height * 3; // BGR8Packed
var frameData = new byte[frameSize];
// 填充模拟图像数据(简单的渐变图案)
var random = new Random();
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>
private Result GetDeviceInfo()
{
try
{
if (_deviceHandle == IntPtr.Zero || !_deviceInfo.HasValue)
{
return Result.Fail("HIK_DEVICE_NOT_CONNECTED", "设备未连接");
}
var info = _deviceInfo.Value;
// 获取设备型号名称
var modelName = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.chModelName) ?? "Unknown";
// 获取设备序列号
var serialNumber = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.chSerialNumber) ?? "Unknown";
// 获取设备用户定义名称
var userDefinedName = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.chUserDefinedName) ?? "Unknown";
// 获取固件版本
var firmwareVersion = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.chFirmwareVersion) ?? "Unknown";
// 获取IP地址
var ipAddress = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.nCurrentIp) ?? "Unknown";
// 获取子网掩码
var subnetMask = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.nCurrentSubNet) ?? "Unknown";
// 获取默认网关
var defaultGateway = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.nDefaultGate) ?? "Unknown";
// 获取MAC地址
var macAddress = Marshal.PtrToStringAnsi(info.SpecialInfo.stGigEInfo.nCurrentMac) ?? "Unknown";
// 更新当前设备信息
if (_currentDevice != null)
{
_currentDevice.DeviceName = userDefinedName;
_currentDevice.FirmwareVersion = firmwareVersion;
_currentDevice.SubnetMask = subnetMask;
_currentDevice.DefaultGateway = defaultGateway;
_currentDevice.MacAddress = macAddress;
}
_logger.LogDebug("设备信息: {ModelName}, {SerialNumber}, {IpAddress}", modelName, serialNumber, ipAddress);
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_GET_DEVICE_INFO_FAILED", "获取设备信息失败。", traceId);
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <summary>
/// 模拟连接。
/// </summary>
private Result MockConnect(HikCameraDevice device)
{
_currentDevice = new HikCameraDevice
{
SerialNumber = device.SerialNumber,
DeviceName = device.DeviceName,
IpAddress = device.IpAddress,
MacAddress = device.MacAddress,
IsConnected = true,
ConnectedAt = DateTime.UtcNow
};
_isConnected = true;
_logger.LogInformation("模拟连接成功: {SerialNumber}", device.SerialNumber);
OnConnectionStateChanged?.Invoke(true, $"模拟连接到 {device.DeviceName}");
return Result.Success(message: $"相机 {device.DeviceName} 模拟连接成功。");
}
/// <summary>
/// 获取模拟设备。
/// </summary>
private Result<IReadOnlyList<HikCameraDevice>> GetMockDevices()
{
var mockDevices = new List<HikCameraDevice>
{
new()
{
SerialNumber = "MOCK_HIK_001",
DeviceName = "模拟海康相机_001",
IpAddress = "192.168.1.100",
MacAddress = "00:11:22:33:44:55",
IsConnected = true
},
new()
{
SerialNumber = "MOCK_HIK_002",
DeviceName = "模拟海康相机_002",
IpAddress = "192.168.1.101",
MacAddress = "00:11:22:33:44:56",
IsConnected = true
}
};
_logger.LogInformation("返回 {Count} 个模拟设备", mockDevices.Count);
return Result.Success<IReadOnlyList<HikCameraDevice>>(mockDevices);
}
/// <summary>
/// 获取像素格式值。
/// </summary>
private uint GetPixelFormatValue(string pixelFormat)
{
return pixelFormat.ToUpperInvariant() switch
{
"BGR8PACKED" => MyCamera.MV_Gvsp_RGB8_Packed,
"RGB8PACKED" => MyCamera.MV_Gvsp_RGB8_Packed,
"MONO8" => MyCamera.MV_Gvsp_Mono8,
"MONO16" => MyCamera.MV_Gvsp_Mono16,
_ => MyCamera.MV_Gvsp_RGB8_Packed
};
}
/// <summary>
/// 自动重连。
/// </summary>
private async Task AutoReconnectAsync()
{
if (_reconnectAttempts >= _options.AutoReconnectCount)
{
_logger.LogWarning("已达到最大重连次数,停止重连");
return;
}
_reconnectAttempts++;
_logger.LogInformation("执行第 {Attempt} 次自动重连", _reconnectAttempts);
await Task.Delay(_options.AutoReconnectIntervalMs);
if (_currentDevice != null)
{
var connectResult = ConnectToDevice(_currentDevice.SerialNumber);
if (connectResult.Succeeded)
{
_logger.LogInformation("自动重连成功");
_reconnectAttempts = 0;
}
else
{
_logger.LogWarning("自动重连失败: {Error}", connectResult.Message);
await AutoReconnectAsync();
}
}
}
/// <summary>
/// 释放资源。
/// </summary>
public void Dispose()
{
try
{
// 停止定时器
_frameTimer?.Dispose();
_frameTimer = null;
// 断开连接
Disconnect();
// 释放SDK
if (_sdkInitialized && _options.RunMode == "Real")
{
_camera?.MV_CC_Finalize_NET();
_camera?.Dispose();
_camera = null;
_sdkInitialized = false;
}
_logger.LogInformation("海康相机服务已释放");
}
catch (Exception ex)
{
_logger.LogError(ex, "释放海康相机服务资源时发生错误");
}
}
}