using MvCamCtrl.NET;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TeamAAS_VP.Core.Cameras;
using TeamAAS_VP.Interfaces;
using TeamAAS_VP.Models;
namespace TeamAAS_VP.Services
{
///
/// 提供相机对象的创建、管理、查找和释放功能的服务类。
/// 线程安全:内部使用 进行并发访问同步。
/// 资源管理:实现 ,在释放时会关闭并清理所有已注册相机。
///
public class CameraService : ICameraService, IDisposable
{
///
/// 保存相机实例的字典,键为相机的唯一标识 。
///
private readonly Dictionary _CameraCollection;
///
/// 用于保证对 并发访问的同步对象。
///
private readonly object _sync = new object();
///
/// 初始化一个新的 实例。
///
public CameraService()
{
_CameraCollection = new Dictionary();
}
///
/// 创建并注册一个相机实例。如果同一 已存在,则会先尝试关闭并替换之。
///
/// 要注册的相机唯一标识符。
/// 用于创建相机的配置信息,不能为空。
/// 始终返回 true(当前实现不表示创建失败)。
/// 为 null 时抛出。
public bool CreateCamera(Guid id, CameraInfo cameraInfo)
{
if (cameraInfo == null) throw new ArgumentNullException(nameof(cameraInfo));
var camera = CreateCameraInstance(id, cameraInfo);
lock (_sync)
{
if (_CameraCollection.ContainsKey(id))
{
var currenCamera = _CameraCollection[id];
try
{
currenCamera.CloseDevice();
}
catch
{
// 忽略关闭异常
}
}
_CameraCollection[id] = camera;
}
return true;
}
///
/// 异步创建并注册一个相机实例(内部使用 Task.Run 包装同步方法)。
///
/// 相机唯一标识符。
/// 相机配置信息。
/// 表示创建结果的任务,结果为布尔值。
public Task CreateCameraAsync(Guid id, CameraInfo cameraInfo)
{
return Task.Run(() => CreateCamera(id, cameraInfo));
}
///
/// 根据 获取已注册的相机实例,如果未找到则返回 null。
///
/// 相机唯一标识符。
/// 找到的 实例或 null。
public ICamera GetCamera(Guid id)
{
lock (_sync)
{
if (_CameraCollection.TryGetValue(id, out var cam))
return cam;
}
return null;
}
///
/// 异步获取相机实例(包装同步调用)。
///
/// 相机唯一标识符。
/// 表示查找结果的任务,任务结果为 或 null。
public Task GetCameraAsync(Guid id)
{
return Task.Run(() =>
{
return GetCamera(id);
});
}
///
/// 取消注册指定 的相机并尝试关闭其设备资源。
/// 如果不存在则无操作。
///
/// 要取消注册的相机标识符。
public void UnRegisterCamera(Guid id)
{
ICamera camera = null;
lock (_sync)
{
if (_CameraCollection.TryGetValue(id, out camera))
{
_CameraCollection.Remove(id);
}
}
if (camera != null)
{
try
{
camera.CloseDevice();
}
catch
{
// 忽略关闭异常
}
}
}
///
/// 异步取消注册相机(包装同步调用)。
///
/// 相机唯一标识符。
/// 表示操作完成的任务。
public Task UnRegisterCameraAsync(Guid id)
{
return Task.Run(() => UnRegisterCamera(id));
}
///
/// 获取当前已注册的所有相机的只读集合快照。
///
/// 只读集合,包含当前注册的所有相机实例。
public IReadOnlyCollection GetAllCameras()
{
lock (_sync)
{
return _CameraCollection.Values.ToList().AsReadOnly();
}
}
///
/// 异步获取所有已注册相机(包装同步调用)。
///
/// 表示只读相机集合的任务。
public Task> GetAllCamerasAsync()
{
return Task.Run(() =>
{
return GetAllCameras();
});
}
///
/// 尝试根据 获取相机实例。
///
/// 相机唯一标识符。
/// 若找到则输出相机实例,否则为 null。
/// 找到返回 true,否则返回 false。
public bool TryGetCamera(Guid id, out ICamera camera)
{
lock (_sync)
{
return _CameraCollection.TryGetValue(id, out camera);
}
}
///
/// 异步尝试获取相机实例,返回一个包含是否找到及相机实例的元组。
///
/// 相机唯一标识符。
/// 任务结果为 (found, camera) 元组。
public Task<(bool found, ICamera camera)> TryGetCameraAsync(Guid id)
{
return Task.Run(() =>
{
ICamera camera;
bool found;
lock (_sync)
{
found = _CameraCollection.TryGetValue(id, out camera);
}
return (found, camera);
});
}
///
/// 判断是否包含指定 的相机注册项。
///
/// 相机唯一标识符。
/// 存在返回 true,否则返回 false。
public bool ContainsCamera(Guid id)
{
lock (_sync)
{
return _CameraCollection.ContainsKey(id);
}
}
///
/// 异步判断相机是否存在(包装同步调用)。
///
/// 相机唯一标识符。
/// 表示存在性的任务结果(布尔值)。
public Task ContainsCameraAsync(Guid id)
{
return Task.Run(() =>
{
return ContainsCamera(id);
});
}
///
/// 移除指定 的相机并关闭其设备资源(如存在)。
///
/// 要移除的相机标识符。
/// 如果存在并已移除返回 true,否则返回 false。
public bool RemoveCamera(Guid id)
{
ICamera camera = null;
lock (_sync)
{
if (_CameraCollection.TryGetValue(id, out camera))
{
_CameraCollection.Remove(id);
}
}
if (camera != null)
{
try
{
camera.CloseDevice();
}
catch
{
// 忽略关闭异常
}
return true;
}
return false;
}
///
/// 异步移除相机(包装同步调用)。
///
/// 相机唯一标识符。
/// 表示操作是否成功的任务。
public Task RemoveCameraAsync(Guid id)
{
return Task.Run(() =>
{
return RemoveCamera(id);
});
}
///
/// 根据相机编号查找相机实例,找不到返回 null。
/// 该方法内部使用反射来兼容不同相机实现中编号属性的命名差异。
///
/// 相机的编号(业务编号、索引等)。
/// 找到的 实例或 null。
public ICamera GetCameraByNumber(int number)
{
if (TryFindCameraByNumber(number, out _, out var camera))
return camera;
return null;
}
///
/// 异步根据编号获取相机实例(包装同步调用)。
///
/// 相机编号。
/// 表示相机实例的任务,未找到为 null。
public Task GetCameraByNumberAsync(int number)
{
return Task.Run(() =>
{
return GetCameraByNumber(number);
});
}
///
/// 异步根据编号取消注册相机(包装同步实现)。
///
/// 相机编号。
/// 表示操作完成的任务。
public Task UnRegisterCameraByNumberAsync(int number)
{
return Task.Run(() =>
{
UnRegisterCameraByNumber(number);
});
}
///
/// 根据编号查找并取消注册相机(如果找到则调用 )。
///
/// 相机编号。
public void UnRegisterCameraByNumber(int number)
{
if (TryFindCameraByNumber(number, out var id, out var camera))
{
UnRegisterCamera(id);
}
}
///
/// 移除并关闭所有已注册的相机,逐个移除以确保资源被正确释放。
///
public void RemoveAllCameras()
{
//依次移除每一个相机,确保资源释放
List cameraIds;
lock (_sync)
{
cameraIds = _CameraCollection.Keys.ToList();
}
foreach (var id in cameraIds)
{
RemoveCamera(id);
}
}
///
/// 使用提供的相机配置信息数组初始化并尝试打开所有相机。
/// 该方法会先清除已有相机,再为每个配置创建实例并尝试打开设备。
///
/// 要初始化的相机信息数组。
///
/// 返回元组,IsSucceed 表示是否所有相机均打开成功;Message 为失败信息或成功消息。
///
public (bool IsSucceed, string Message) InitializeAllCameras(CameraInfo[] cameras)
{
//清除现有相机
RemoveAllCameras();
foreach (var camInfo in cameras)
{
var camera = CreateCameraInstance(camInfo.Id, camInfo);
lock (_sync)
{
_CameraCollection[camInfo.Id] = camera;
}
}
bool allConnected = true;
StringBuilder errorMessages = new StringBuilder();
//获取所有相机并尝试打开设备
foreach (var camInfo in cameras)
{
if (TryGetCamera(camInfo.Id, out var camera))
{
bool opened = camera.OpenDevice();
if (!opened)
{
allConnected = false;
errorMessages.AppendLine($"相机 {camInfo.CameraName}(编号: {camInfo.CameraNo})打开失败: {camera.ErrorMessage}");
}
}
else
{
allConnected = false;
errorMessages.AppendLine($"未找到相机 {camInfo.CameraName}(编号: {camInfo.CameraNo})的实例。");
}
}
if (allConnected)
{
return (true, "所有相机均已成功初始化并连接。");
}
else
{
return (false, errorMessages.ToString());
}
}
///
/// 异步初始化所有相机(包装同步实现)。
///
/// 相机信息数组。
/// 返回包含是否成功及消息的任务。
public Task<(bool IsSucceed, string Message)> InitializeAllCamerasAsync(CameraInfo[] cameras)
{
return Task.Run(() => InitializeAllCameras(cameras));
}
///
/// 更新指定相机的业务编号(Index)。若未找到对应相机则无操作。
///
/// 相机唯一标识符。
/// 新的编号值。
public void UpdateCameraNumber(Guid id, int newNumber)
{
if (TryGetCamera(id, out var camera))
{
camera.Index = newNumber;
}
}
///
/// 根据提供的 创建具体相机实现的实例。
/// 支持不同品牌的相机类型分发;默认使用 。
///
/// 相机唯一标识符。
/// 相机配置信息。
/// 创建的 实例(未打开设备)。
private ICamera CreateCameraInstance(Guid id, CameraInfo cameraInfo)
{
ICamera camera;
if (cameraInfo.CameraBrand == Enums.CameraBrand.Basler_Pylon)
{
camera = new BaslerCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else if (cameraInfo.CameraBrand == Enums.CameraBrand.HikRobot_MVS)
{
camera = new MvCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else if (cameraInfo.CameraBrand == Enums.CameraBrand.IRayple)
{
camera = new AHuaCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else if (cameraInfo.CameraBrand == Enums.CameraBrand.Cognex)
{
camera = new CognexCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else if (cameraInfo.CameraBrand == Enums.CameraBrand.Galaxy)
{
camera = new GalaxyCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else if (cameraInfo.CameraBrand == Enums.CameraBrand.Opt)
{
camera = new OptCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
else
{
camera = new MvCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
}
return camera;
}
///
/// 根据业务编号在已注册相机中查找匹配项。
/// 实现要点:
/// - 对每个相机尝试通过常见属性名("CameraNo", "Number", "No")读取编号;
/// - 若未直接在相机对象找到编号,则尝试从其可能的 CameraInfo / Info 属性中读取;
/// - 使用 Convert.ToInt32 进行通用转换,忽略无法转换或异常的项;
/// - 在找到匹配编号时返回 true,并输出相机 id 与实例;否则返回 false。
///
/// 要匹配的编号。
/// 若找到则输出相机的 标识符,未找到为 。
/// 若找到则输出对应的 实例,否则为 null。
/// 找到匹配项返回 true,否则返回 false。
private bool TryFindCameraByNumber(int number, out Guid id, out ICamera camera)
{
lock (_sync)
{
foreach (var kvp in _CameraCollection)
{
var c = kvp.Value;
if (c == null) continue;
object rawValue = null;
var type = c.GetType();
var prop = type.GetProperty("CameraNo") ?? type.GetProperty("Number") ?? type.GetProperty("No");
if (prop != null)
{
try
{
rawValue = prop.GetValue(c);
}
catch
{
rawValue = null;
}
}
if (rawValue == null)
{
var infoProp = type.GetProperty("CameraInfo") ?? type.GetProperty("Info");
if (infoProp != null)
{
try
{
var infoObj = infoProp.GetValue(c);
if (infoObj != null)
{
var infoType = infoObj.GetType();
var numberProp = infoType.GetProperty("CameraNo") ?? infoType.GetProperty("CameraNumber") ?? infoType.GetProperty("Number") ?? infoType.GetProperty("No");
if (numberProp != null)
{
try
{
rawValue = numberProp.GetValue(infoObj);
}
catch
{
rawValue = null;
}
}
}
}
catch
{
rawValue = null;
}
}
}
if (rawValue != null)
{
int parsed;
try
{
if (rawValue is int) parsed = (int)rawValue;
else parsed = Convert.ToInt32(rawValue);
}
catch
{
continue;
}
if (parsed == number)
{
id = kvp.Key;
camera = c;
return true;
}
}
}
}
id = Guid.Empty;
camera = null;
return false;
}
///
/// 释放服务占用的资源:关闭所有相机设备并清空内部集合。
///
public void Dispose()
{
List cameras;
lock (_sync)
{
cameras = _CameraCollection.Values.ToList();
_CameraCollection.Clear();
}
foreach (var c in cameras)
{
try
{
c.CloseDevice();
}
catch
{
// 忽略单个相机关闭异常
}
}
GC.SuppressFinalize(this);
}
}
}