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