SettingViewModel.Camera.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Drawing;
  5. using System.Windows;
  6. using System.Windows.Interop;
  7. using System.Windows.Media.Imaging;
  8. using TeamAAS.Camera;
  9. using TeamAAS.Camera.Images;
  10. using TeamAAS.Camera.Interfaces;
  11. using TeamAAS.Camera.Models;
  12. using TeamAAS.Camera.UI;
  13. using TeamAAS.Views;
  14. using System.Linq;
  15. namespace TeamAAS.ViewModels
  16. {
  17. /// <summary>设置页 - 相机管理(属性 + 操作)</summary>
  18. public partial class SettingViewModel
  19. {
  20. #region 相机管理
  21. public ObservableCollection<CameraInfo> Cameras { get; }
  22. public ObservableCollection<SdkStatusItem> SdkStatus { get; }
  23. private CameraInfo _selectedCamera;
  24. public CameraInfo SelectedCamera
  25. {
  26. get { return _selectedCamera; }
  27. set
  28. {
  29. if (SetProperty(ref _selectedCamera, value))
  30. RefreshDebugHost();
  31. RaisePropertyChanged(nameof(HasSelectedCamera));
  32. }
  33. }
  34. public bool HasSelectedCamera => _selectedCamera != null;
  35. private ICamera _selectedCameraDevice;
  36. /// <summary>选中相机的运行时实例——内联属性编辑器直接编辑它(修改即实时下发硬件),不再走双击弹窗</summary>
  37. public ICamera SelectedCameraDevice
  38. {
  39. get { return _selectedCameraDevice; }
  40. private set { SetProperty(ref _selectedCameraDevice, value); }
  41. }
  42. /// <summary>当前正在采集的相机实例——设置页同一时间只允许一路预览,防止多路画面串页</summary>
  43. private ICamera _grabbingCamera;
  44. private object _currentDebugPage;
  45. /// <summary>右侧调试页容器的内容:按 [Camera] 特性加载品牌专属页,未指定则用默认页</summary>
  46. public object CurrentDebugPage
  47. {
  48. get { return _currentDebugPage; }
  49. private set { SetProperty(ref _currentDebugPage, value); }
  50. }
  51. /// <summary>按选中的相机刷新右侧调试页与属性编辑器的绑定目标</summary>
  52. private void RefreshDebugHost()
  53. {
  54. // 旧页面解绑
  55. (_currentDebugPage as ICameraDebugPage)?.UnbindCamera();
  56. CurrentDebugPage = null;
  57. SelectedCameraDevice = null;
  58. if (_selectedCamera == null)
  59. {
  60. StopGrabbingInternal(_grabbingCamera);
  61. return;
  62. }
  63. var device = _cameraManager.GetCamera(_selectedCamera.Id);
  64. if (device == null)
  65. {
  66. StopGrabbingInternal(_grabbingCamera);
  67. return;
  68. }
  69. SelectedCameraDevice = device;
  70. // 切到另一台相机时停掉上一路采集,避免旧相机画面串进新调试页
  71. if (_grabbingCamera != null && !ReferenceEquals(_grabbingCamera, device))
  72. StopGrabbingInternal(_grabbingCamera);
  73. try
  74. {
  75. CurrentDebugPage = CameraDebugPageFactory.Create(device);
  76. }
  77. catch (Exception ex)
  78. {
  79. System.Diagnostics.Debug.WriteLine($"加载调试页失败: {ex.Message}");
  80. }
  81. }
  82. private BitmapSource _currentImage;
  83. public BitmapSource CurrentImage
  84. {
  85. get { return _currentImage; }
  86. set { SetProperty(ref _currentImage, value); }
  87. }
  88. private string _fpsText = "";
  89. public string FpsText
  90. {
  91. get { return _fpsText; }
  92. set { SetProperty(ref _fpsText, value); }
  93. }
  94. #endregion
  95. #region 相机操作
  96. private void LoadSdkStatus()
  97. {
  98. SdkStatus.Clear();
  99. try
  100. {
  101. var statusList = CameraManager.Instance.GetSdkStatus();
  102. foreach (var (Name, Installed, Version) in statusList)
  103. {
  104. SdkStatus.Add(new SdkStatusItem { Name = Name, Installed = Installed, Version = Version });
  105. }
  106. }
  107. catch { }
  108. }
  109. private void OpenSearchDialog()
  110. {
  111. var dialog = new SearchDeviceWindow
  112. {
  113. Owner = Application.Current.MainWindow
  114. };
  115. if (dialog.ShowDialog() == true)
  116. {
  117. foreach (var info in dialog.SelectedDevices)
  118. {
  119. try
  120. {
  121. if (_cameraManager.ContainsSN(info.SerialNumber))
  122. {
  123. DialogHelper.ShowWarning($"相机 {info.CameraName} 已存在");
  124. continue;
  125. }
  126. _cameraManager.CreateCamera(info.Id, info);
  127. info.CameraNo = Cameras.Count + 1;
  128. Cameras.Add(info);
  129. }
  130. catch (Exception ex)
  131. {
  132. DialogHelper.ShowError("添加相机失败", ex);
  133. }
  134. }
  135. }
  136. }
  137. private void DeleteCamera()
  138. {
  139. if (SelectedCamera == null) return;
  140. if (!DialogHelper.ConfirmYesNo(
  141. $"确认删除相机 \"{SelectedCamera.CameraName}\" 吗?",
  142. "确认删除"))
  143. {
  144. return;
  145. }
  146. var cameraToRemove = SelectedCamera;
  147. var id = cameraToRemove.Id;
  148. // 删除前先停掉该相机的采集与预览(退订帧事件),避免残留订阅推鬼帧
  149. if (_grabbingCamera != null && _grabbingCamera.ID == id)
  150. StopGrabbingInternal(_grabbingCamera);
  151. try
  152. {
  153. _cameraManager.RemoveCamera(id);
  154. }
  155. catch (Exception ex)
  156. {
  157. System.Diagnostics.Debug.WriteLine($"释放相机资源异常: {ex.Message}");
  158. }
  159. Cameras.Remove(cameraToRemove);
  160. for (int i = 0; i < Cameras.Count; i++)
  161. {
  162. Cameras[i].CameraNo = i + 1;
  163. }
  164. SelectedCamera = null;
  165. }
  166. private async System.Threading.Tasks.Task ConnectAsync()
  167. {
  168. if (SelectedCamera == null) return;
  169. try
  170. {
  171. var camera = _cameraManager.GetCamera(SelectedCamera.Id);
  172. if (camera == null)
  173. {
  174. DialogHelper.ShowWarning("未找到相机实例,请先添加");
  175. return;
  176. }
  177. bool success = await camera.OpenDeviceAsync();
  178. SelectedCamera.IsConnected = success;
  179. if (!success)
  180. {
  181. DialogHelper.ShowError($"相机连接失败: {camera.ErrorMessage}");
  182. }
  183. }
  184. catch (Exception ex)
  185. {
  186. DialogHelper.ShowError("连接失败", ex);
  187. }
  188. }
  189. private void Disconnect()
  190. {
  191. if (SelectedCamera == null) return;
  192. try
  193. {
  194. var camera = _cameraManager.GetCamera(SelectedCamera.Id);
  195. if (camera == null) return;
  196. if (camera.IsGrabbing)
  197. {
  198. camera.StopGrabbing();
  199. camera.ImageCallbackEvent -= OnImageCallback;
  200. SelectedCamera.IsGrabbing = false;
  201. }
  202. camera.CloseDevice();
  203. SelectedCamera.IsConnected = false;
  204. CurrentImage = null;
  205. FpsText = "";
  206. if (CurrentDebugPage is ICameraImageSink sinkClear)
  207. sinkClear.ShowImage(null);
  208. }
  209. catch (Exception ex)
  210. {
  211. DialogHelper.ShowError("断开失败", ex);
  212. }
  213. }
  214. private void StartGrabbing()
  215. {
  216. if (SelectedCamera == null) return;
  217. try
  218. {
  219. var camera = _cameraManager.GetCamera(SelectedCamera.Id);
  220. if (camera == null) return;
  221. // 同一时间只允许一路预览:采集另一台相机前先停掉当前这一路,
  222. // 否则旧相机的帧订阅仍在,两路画面会交替串进同一个调试页
  223. if (_grabbingCamera != null && !ReferenceEquals(_grabbingCamera, camera))
  224. StopGrabbingInternal(_grabbingCamera);
  225. if (!camera.IsConnected)
  226. {
  227. camera.OpenDevice();
  228. SelectedCamera.IsConnected = camera.IsConnected;
  229. }
  230. camera.ImageCallbackEvent += OnImageCallback;
  231. camera.StartGrabbing();
  232. SelectedCamera.IsGrabbing = true;
  233. _grabbingCamera = camera;
  234. }
  235. catch (Exception ex)
  236. {
  237. DialogHelper.ShowError("开始采集失败", ex);
  238. }
  239. }
  240. private void StopGrabbing()
  241. {
  242. var camera = _cameraManager.GetCamera(SelectedCamera?.Id ?? Guid.Empty);
  243. StopGrabbingInternal(camera ?? _grabbingCamera);
  244. }
  245. /// <summary>停掉指定相机的采集并退订帧事件(按 Id 同步列表行的采集状态标记)</summary>
  246. private void StopGrabbingInternal(ICamera camera)
  247. {
  248. if (camera == null) return;
  249. try
  250. {
  251. camera.StopGrabbing();
  252. camera.ImageCallbackEvent -= OnImageCallback;
  253. var info = Cameras?.FirstOrDefault(c => c.Id == camera.ID);
  254. if (info != null)
  255. {
  256. info.IsGrabbing = false;
  257. info.IsConnected = camera.IsConnected;
  258. }
  259. if (info != null && ReferenceEquals(SelectedCamera, info))
  260. {
  261. SelectedCamera.IsGrabbing = false;
  262. CurrentImage = null;
  263. FpsText = "";
  264. }
  265. }
  266. catch (Exception ex)
  267. {
  268. DialogHelper.ShowError("停止采集失败", ex);
  269. }
  270. finally
  271. {
  272. if (ReferenceEquals(_grabbingCamera, camera))
  273. _grabbingCamera = null;
  274. }
  275. }
  276. private void OnImageCallback(GenImage image, TimeSpan elapsed, string info)
  277. {
  278. if (image == null) return;
  279. try
  280. {
  281. var bitmap = image.ToBitmap();
  282. var hBitmap = bitmap.GetHbitmap();
  283. var bmpSource = Imaging.CreateBitmapSourceFromHBitmap(
  284. hBitmap, IntPtr.Zero, Int32Rect.Empty,
  285. BitmapSizeOptions.FromEmptyOptions());
  286. DeleteObject(hBitmap);
  287. bitmap.Dispose();
  288. bmpSource.Freeze();
  289. Application.Current?.Dispatcher?.BeginInvoke(new Action(() =>
  290. {
  291. CurrentImage = bmpSource;
  292. FpsText = elapsed.TotalMilliseconds > 0
  293. ? $"{1000.0 / elapsed.TotalMilliseconds:F1} fps"
  294. : "";
  295. // 把画面推送给当前调试页(若其具备图像接收能力)
  296. if (CurrentDebugPage is ICameraImageSink sink)
  297. sink.ShowImage(bmpSource);
  298. }));
  299. }
  300. catch { }
  301. }
  302. #endregion
  303. #region 相机列表刷新 & 配置保存
  304. private void RefreshCameraList()
  305. {
  306. var selectedId = SelectedCamera?.Id ?? Guid.Empty;
  307. Cameras.Clear();
  308. var infos = _cameraManager.GetAllCameraInfos();
  309. foreach (var info in infos)
  310. {
  311. Cameras.Add(info);
  312. }
  313. // 保持原选中项(重建调试页容器与属性编辑器绑定)
  314. if (selectedId != Guid.Empty)
  315. {
  316. var match = Cameras.FirstOrDefault(c => c.Id == selectedId);
  317. if (match != null && !ReferenceEquals(match, SelectedCamera))
  318. SelectedCamera = match;
  319. }
  320. }
  321. private void SaveCameraConfig()
  322. {
  323. try
  324. {
  325. if (_cameraManager.SaveConfig())
  326. {
  327. RefreshCameraList();
  328. DialogHelper.Success("相机配置已保存");
  329. }
  330. else
  331. DialogHelper.ShowError("相机配置保存失败,请查看日志");
  332. }
  333. catch (Exception ex)
  334. {
  335. DialogHelper.ShowError("保存相机配置失败", ex);
  336. }
  337. }
  338. #endregion
  339. }
  340. }