using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TeamAAS.Camera.Attributes;
using TeamAAS.Camera.Enums;
using TeamAAS.Camera.Interfaces;
using TeamAAS.Camera.Models;
namespace TeamAAS.Camera
{
///
/// 相机生命周期和配置管理器。
/// 负责相机实例的创建、注册、连接、采集控制、移除、JSON 配置持久化以及品牌/设备发现。
/// 同时作为懒汉单例使用:CameraManager.Instance。
///
public class CameraManager : ICameraManager
{
#region 单例
private static readonly Lazy _instance =
new Lazy(() => new CameraManager(), isThreadSafe: true);
///
/// 懒汉单例入口。首次访问时初始化,线程安全。
///
public static CameraManager Instance => _instance.Value;
#endregion
#region 字段
private readonly Dictionary _cameras;
private readonly object _sync = new object();
// 设备发现 provider 缓存
private List _providers;
private readonly object _providerLock = new object();
// 反射扫描结果缓存
private List _scannedTypes;
private readonly object _scanLock = new object();
#endregion
#region 构造
///
/// 默认构造(供单例和 DI 容器使用)。
/// 外部不应直接 new,请使用 或通过 DI 注入 。
///
public CameraManager()
{
_cameras = new Dictionary();
}
#endregion
#region 相机生命周期
public bool CreateCamera(Guid id, CameraInfo cameraInfo)
{
if (cameraInfo == null) throw new ArgumentNullException(nameof(cameraInfo));
var camera = CreateCameraInstance(id, cameraInfo);
ICamera existing = null;
lock (_sync)
{
_cameras.TryGetValue(id, out existing);
_cameras[id] = camera;
}
if (existing != null && !ReferenceEquals(existing, camera))
{
try
{
if (existing.IsGrabbing)
existing.StopGrabbing();
}
catch { }
try { existing.CloseDevice(); } catch { }
}
return true;
}
public Task CreateCameraAsync(Guid id, CameraInfo cameraInfo)
{
return Task.Run(() => CreateCamera(id, cameraInfo));
}
public ICamera GetCamera(Guid id)
{
lock (_sync)
{
return _cameras.TryGetValue(id, out var cam) ? cam : null;
}
}
public Task GetCameraAsync(Guid id)
{
return Task.Run(() => GetCamera(id));
}
public IReadOnlyCollection GetAllCameras()
{
lock (_sync)
{
return _cameras.Values.ToList().AsReadOnly();
}
}
public Task> GetAllCamerasAsync()
{
return Task.Run(() => GetAllCameras());
}
public bool TryGetCamera(Guid id, out ICamera camera)
{
lock (_sync)
{
return _cameras.TryGetValue(id, out camera);
}
}
public IReadOnlyList Devices
{
get
{
lock (_sync)
{
return _cameras.Values.ToList().AsReadOnly();
}
}
}
public bool ContainsCamera(Guid id)
{
lock (_sync)
{
return _cameras.ContainsKey(id);
}
}
public bool ContainsSN(string serialNumber)
{
lock (_sync)
{
return _cameras?.Values.Any(c => c.SerialNumber == serialNumber) ?? false;
}
}
public bool RemoveCamera(Guid id)
{
ICamera camera = null;
lock (_sync)
{
if (_cameras.TryGetValue(id, out camera))
{
_cameras.Remove(id);
}
}
if (camera != null)
{
try
{
if (camera.IsGrabbing)
camera.StopGrabbing();
}
catch { }
try
{
camera.CloseDevice();
}
catch { }
return true;
}
return false;
}
public Task RemoveCameraAsync(Guid id)
{
return Task.Run(() => RemoveCamera(id));
}
public void UnRegisterCamera(Guid id)
{
RemoveCamera(id);
}
///
/// 停止采集并关闭所有相机(程序退出前调用)。
/// 幂等:已关闭的设备再调一次不会抛异常。
///
public void CloseAll()
{
List list;
lock (_sync)
{
list = _cameras.Values.ToList();
_cameras.Clear();
}
foreach (var camera in list)
{
try { if (camera.IsGrabbing) camera.StopGrabbing(); } catch { }
try { camera.CloseDevice(); } catch { }
}
}
public Task UnRegisterCameraAsync(Guid id)
{
return Task.Run(() => RemoveCamera(id));
}
public ICamera GetCameraByNumber(int number)
{
lock (_sync)
{
foreach (var kvp in _cameras)
{
if (kvp.Value != null && kvp.Value.Index == number)
return kvp.Value;
}
}
return null;
}
public void RemoveAllCameras()
{
List ids;
lock (_sync)
{
ids = _cameras.Keys.ToList();
}
foreach (var id in ids)
{
RemoveCamera(id);
}
}
public (bool IsSucceed, string Message) InitializeAllCameras(CameraInfo[] cameras)
{
RemoveAllCameras();
foreach (var info in cameras)
{
var camera = CreateCameraInstance(info.Id, info);
lock (_sync)
{
_cameras[info.Id] = camera;
}
}
bool allConnected = true;
StringBuilder errors = new StringBuilder();
foreach (var info in cameras)
{
if (TryGetCamera(info.Id, out var camera))
{
bool opened = camera.OpenDevice();
if (!opened)
{
allConnected = false;
errors.AppendLine($"相机 {info.CameraName}(编号: {info.CameraNo})打开失败: {camera.ErrorMessage}");
}
else
{
// 应用配置里持久化的参数快照(曝光/增益/Gamma),null 表示沿用硬件默认值
ApplySavedParameters(camera, info);
}
}
else
{
allConnected = false;
errors.AppendLine($"未找到相机 {info.CameraName}(编号: {info.CameraNo})的实例。");
}
}
return allConnected ? (true, "所有相机均已成功初始化并连接。") : (false, errors.ToString());
}
public Task<(bool IsSucceed, string Message)> InitializeAllCamerasAsync(CameraInfo[] cameras)
{
return Task.Run(() => InitializeAllCameras(cameras));
}
public void UpdateCameraNumber(Guid id, int newNumber)
{
if (TryGetCamera(id, out var camera))
{
camera.Index = newNumber;
}
}
#endregion
#region 配置持久化
public static string DefaultConfigPath
{
get
{
return Path.Combine(TeamAAS.PathHelper.ConfigDirectory, "Cameras.json");
}
}
public bool SaveConfig(string path = null)
{
path = path ?? DefaultConfigPath;
var infos = new List();
lock (_sync)
{
foreach (var cam in _cameras.Values)
{
infos.Add(new CameraInfo
{
Id = cam.ID,
CameraNo = cam.Index,
CameraName = cam.Name,
CameraBrand = cam.CameraBrand,
CameraType = cam.CameraType,
CameraIp = cam.CameraIp,
IsFindByIp = cam.IsFindByIp,
SerialNumber = cam.SerialNumber,
Model = cam.ModelName,
ManufacturerName = cam.ManufacturerName,
ExposureTime = cam.ExposureTime,
Gain = cam.Gain,
Gamma = cam.Gamma,
});
}
}
return TeamAAS.JsonFileStore.Save(path, infos);
}
public IReadOnlyList GetAllCameraInfos()
{
var infos = new List();
lock (_sync)
{
foreach (var cam in _cameras.Values)
{
infos.Add(new CameraInfo
{
Id = cam.ID,
CameraNo = cam.Index,
CameraName = cam.Name,
CameraBrand = cam.CameraBrand,
CameraType = cam.CameraType,
CameraIp = cam.CameraIp,
IsFindByIp = cam.IsFindByIp,
SerialNumber = cam.SerialNumber,
Model = cam.ModelName,
ManufacturerName = cam.ManufacturerName,
ExposureTime = cam.ExposureTime,
Gain = cam.Gain,
Gamma = cam.Gamma,
});
}
}
return infos.AsReadOnly();
}
public void LoadConfig(string path = null)
{
path = path ?? DefaultConfigPath;
if (!File.Exists(path)) return;
var infos = TeamAAS.JsonFileStore.Load>(path);
if (infos == null || infos.Count == 0) return;
InitializeAllCameras(infos.ToArray());
}
#endregion
#region 品牌/设备发现(原 CameraFactory 职责)
///
/// 获取所有已注册的相机品牌 provider(首次访问时反射扫描并缓存)。
///
private List GetProviders()
{
if (_providers != null) return _providers;
lock (_providerLock)
{
if (_providers != null) return _providers;
_providers = ScanProviders();
return _providers;
}
}
private List ScanProviders()
{
var result = new List();
// 通用模块扫描:收集全部 ICamera 实现(含 Runtime\Plugins 里的插件 DLL)
foreach (var type in TeamAAS.Modularity.AssemblyScanner.FindImplementations())
{
var attr = type.GetCustomAttribute();
if (attr == null) continue;
var getDevicesMethod = type.GetMethod("GetDevices", BindingFlags.Public | BindingFlags.Static);
var checkInstalledMethod = type.GetMethod("CheckSoftwareInstalled", BindingFlags.Public | BindingFlags.Static);
if (getDevicesMethod == null) continue;
result.Add(new CameraProviderInfo
{
CameraType = type,
Brand = attr.Brand,
DisplayName = attr.DisplayName,
GetDevicesDelegate = () =>
{
try
{
var r = getDevicesMethod.Invoke(null, null);
if (r is CameraInfo[] arr) return arr.ToList();
if (r is IEnumerable list) return list.ToList();
return new List();
}
catch { return new List(); }
},
CheckInstalledDelegate = () =>
{
if (checkInstalledMethod == null)
return (false, "");
try
{
var r = checkInstalledMethod.Invoke(null, null);
if (r is ValueTuple tuple) return tuple;
if (r is Tuple t2) return (t2.Item1, t2.Item2);
return (false, "");
}
catch { return (false, ""); }
}
});
}
return result
.GroupBy(p => p.CameraType.FullName)
.Select(g => g.First())
.OrderBy(p => p.DisplayName)
.ToList();
}
///
/// 搜索所有品牌的可用相机设备
///
public List DiscoverAllDevices()
{
var allDevices = new List();
int index = 1;
foreach (var provider in GetProviders())
{
try
{
var devices = provider.GetDevicesDelegate();
foreach (var d in devices)
{
d.CameraNo = index++;
allDevices.Add(d);
}
}
catch { }
}
return allDevices;
}
///
/// 按品牌搜索可用相机设备
///
public List DiscoverByBrand(string brand)
{
var provider = GetProviders().FirstOrDefault(p => p.Brand == brand);
if (provider == null)
throw new NotSupportedException($"品牌 {brand} 暂未实现设备发现功能");
try
{
return provider.GetDevicesDelegate();
}
catch
{
return new List();
}
}
///
/// 获取已安装的相机品牌列表(用于弹窗选择)
///
public List<(string Brand, string DisplayName, bool Installed)> GetAvailableBrands()
{
var list = new List<(string, string, bool)>();
foreach (var provider in GetProviders())
{
try
{
var (installed, _) = provider.CheckInstalledDelegate();
list.Add((provider.Brand, provider.DisplayName, installed));
}
catch
{
list.Add((provider.Brand, provider.DisplayName, false));
}
}
return list;
}
///
/// 获取相机品牌的显示名称
///
public string GetBrandDisplayName(string brand)
{
var provider = GetProviders().FirstOrDefault(p => p.Brand == brand);
return provider?.DisplayName ?? brand;
}
///
/// 获取已安装的相机SDK列表
///
public List<(string Name, bool Installed, string Version)> GetSdkStatus()
{
var list = new List<(string, bool, string)>();
foreach (var provider in GetProviders())
{
try
{
var (installed, version) = provider.CheckInstalledDelegate();
list.Add((provider.DisplayName, installed, version));
}
catch
{
list.Add((provider.DisplayName, false, ""));
}
}
return list;
}
#endregion
#region 反射扫描(类型信息)
///
/// 获取所有可用的相机类型信息(通过 [Camera] 特性自动发现)。
///
public IReadOnlyList GetAvailableTypes()
{
if (_scannedTypes != null) return _scannedTypes.AsReadOnly();
lock (_scanLock)
{
if (_scannedTypes != null) return _scannedTypes.AsReadOnly();
_scannedTypes = ScanTypes();
return _scannedTypes.AsReadOnly();
}
}
private List ScanTypes()
{
var result = new List();
// 通用模块扫描:收集全部 ICamera 实现(含 Runtime\Plugins 里的插件 DLL)
foreach (var type in TeamAAS.Modularity.AssemblyScanner.FindImplementations())
{
var attr = type.GetCustomAttribute();
if (attr == null) continue;
result.Add(new CameraTypeInfo
{
TypeKey = type.FullName,
DisplayName = attr.DisplayName,
Brand = attr.Brand,
Description = attr.Description,
Type = type
});
}
return result
.GroupBy(t => t.TypeKey)
.Select(g => g.First())
.OrderBy(t => t.DisplayName)
.ToList();
}
#endregion
#region IDisposable
public void Dispose()
{
RemoveAllCameras();
GC.SuppressFinalize(this);
}
#endregion
#region 私有辅助
private ICamera CreateCameraInstance(Guid id, CameraInfo info)
{
var types = GetAvailableTypes();
var match = types.FirstOrDefault(t => t.Brand == info.CameraBrand);
if (match == null)
throw new NotSupportedException("Unsupported camera brand: " + info.CameraBrand);
return (ICamera)Activator.CreateInstance(
match.Type,
id, info.CameraNo, info.CameraName, info.SerialNumber, info.CameraIp, info.IsFindByIp);
}
///
/// 相机打开成功后,把 Cameras.json 中持久化的参数快照下发到硬件。
/// 属性 setter 内部已做异常兜底(失败写入 ErrorMessage),此处再整体兜底保证初始化流程不被打断。
///
private static void ApplySavedParameters(ICamera camera, CameraInfo info)
{
if (camera == null || info == null) return;
try
{
// 自动曝光是相机品牌特有(UVC)的运行时开关,不在这层恢复;
// 恢复手动参数即可——UVC 处于自动曝光时会自行忽略手动写入。
if (info.ExposureTime.HasValue) camera.ExposureTime = info.ExposureTime.Value;
if (info.Gain.HasValue) camera.Gain = info.Gain.Value;
if (info.Gamma.HasValue) camera.Gamma = info.Gamma.Value;
}
catch { }
}
private class CameraProviderInfo
{
public Type CameraType { get; set; }
public string Brand { get; set; }
public string DisplayName { get; set; }
public Func> GetDevicesDelegate { get; set; }
public Func<(bool isInstalled, string version)> CheckInstalledDelegate { get; set; }
}
#endregion
}
public class CameraTypeInfo
{
public string TypeKey { get; set; }
public string DisplayName { get; set; }
public string Brand { get; set; }
public string Description { get; set; }
public Type Type { get; set; }
}
}