using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; using TeamAAS.Camera; using TeamAAS.Camera.Images; using TeamAAS.Camera.Interfaces; using TeamAAS.Camera.Models; using TeamAAS.Camera.UI; using TeamAAS.Views; using System.Linq; namespace TeamAAS.ViewModels { /// 设置页 - 相机管理(属性 + 操作) public partial class SettingViewModel { #region 相机管理 public ObservableCollection Cameras { get; } public ObservableCollection SdkStatus { get; } private CameraInfo _selectedCamera; public CameraInfo SelectedCamera { get { return _selectedCamera; } set { if (SetProperty(ref _selectedCamera, value)) RefreshDebugHost(); RaisePropertyChanged(nameof(HasSelectedCamera)); } } public bool HasSelectedCamera => _selectedCamera != null; private ICamera _selectedCameraDevice; /// 选中相机的运行时实例——内联属性编辑器直接编辑它(修改即实时下发硬件),不再走双击弹窗 public ICamera SelectedCameraDevice { get { return _selectedCameraDevice; } private set { SetProperty(ref _selectedCameraDevice, value); } } /// 当前正在采集的相机实例——设置页同一时间只允许一路预览,防止多路画面串页 private ICamera _grabbingCamera; private object _currentDebugPage; /// 右侧调试页容器的内容:按 [Camera] 特性加载品牌专属页,未指定则用默认页 public object CurrentDebugPage { get { return _currentDebugPage; } private set { SetProperty(ref _currentDebugPage, value); } } /// 按选中的相机刷新右侧调试页与属性编辑器的绑定目标 private void RefreshDebugHost() { // 旧页面解绑 (_currentDebugPage as ICameraDebugPage)?.UnbindCamera(); CurrentDebugPage = null; SelectedCameraDevice = null; if (_selectedCamera == null) { StopGrabbingInternal(_grabbingCamera); return; } var device = _cameraManager.GetCamera(_selectedCamera.Id); if (device == null) { StopGrabbingInternal(_grabbingCamera); return; } SelectedCameraDevice = device; // 切到另一台相机时停掉上一路采集,避免旧相机画面串进新调试页 if (_grabbingCamera != null && !ReferenceEquals(_grabbingCamera, device)) StopGrabbingInternal(_grabbingCamera); try { CurrentDebugPage = CameraDebugPageFactory.Create(device); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"加载调试页失败: {ex.Message}"); } } private BitmapSource _currentImage; public BitmapSource CurrentImage { get { return _currentImage; } set { SetProperty(ref _currentImage, value); } } private string _fpsText = ""; public string FpsText { get { return _fpsText; } set { SetProperty(ref _fpsText, value); } } #endregion #region 相机操作 private void LoadSdkStatus() { SdkStatus.Clear(); try { var statusList = CameraManager.Instance.GetSdkStatus(); foreach (var (Name, Installed, Version) in statusList) { SdkStatus.Add(new SdkStatusItem { Name = Name, Installed = Installed, Version = Version }); } } catch { } } private void OpenSearchDialog() { var dialog = new SearchDeviceWindow { Owner = Application.Current.MainWindow }; if (dialog.ShowDialog() == true) { foreach (var info in dialog.SelectedDevices) { try { if (_cameraManager.ContainsSN(info.SerialNumber)) { DialogHelper.ShowWarning($"相机 {info.CameraName} 已存在"); continue; } _cameraManager.CreateCamera(info.Id, info); info.CameraNo = Cameras.Count + 1; Cameras.Add(info); } catch (Exception ex) { DialogHelper.ShowError("添加相机失败", ex); } } } } private void DeleteCamera() { if (SelectedCamera == null) return; if (!DialogHelper.ConfirmYesNo( $"确认删除相机 \"{SelectedCamera.CameraName}\" 吗?", "确认删除")) { return; } var cameraToRemove = SelectedCamera; var id = cameraToRemove.Id; // 删除前先停掉该相机的采集与预览(退订帧事件),避免残留订阅推鬼帧 if (_grabbingCamera != null && _grabbingCamera.ID == id) StopGrabbingInternal(_grabbingCamera); try { _cameraManager.RemoveCamera(id); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"释放相机资源异常: {ex.Message}"); } Cameras.Remove(cameraToRemove); for (int i = 0; i < Cameras.Count; i++) { Cameras[i].CameraNo = i + 1; } SelectedCamera = null; } private async System.Threading.Tasks.Task ConnectAsync() { if (SelectedCamera == null) return; try { var camera = _cameraManager.GetCamera(SelectedCamera.Id); if (camera == null) { DialogHelper.ShowWarning("未找到相机实例,请先添加"); return; } bool success = await camera.OpenDeviceAsync(); SelectedCamera.IsConnected = success; if (!success) { DialogHelper.ShowError($"相机连接失败: {camera.ErrorMessage}"); } } catch (Exception ex) { DialogHelper.ShowError("连接失败", ex); } } private void Disconnect() { if (SelectedCamera == null) return; try { var camera = _cameraManager.GetCamera(SelectedCamera.Id); if (camera == null) return; if (camera.IsGrabbing) { camera.StopGrabbing(); camera.ImageCallbackEvent -= OnImageCallback; SelectedCamera.IsGrabbing = false; } camera.CloseDevice(); SelectedCamera.IsConnected = false; CurrentImage = null; FpsText = ""; if (CurrentDebugPage is ICameraImageSink sinkClear) sinkClear.ShowImage(null); } catch (Exception ex) { DialogHelper.ShowError("断开失败", ex); } } private void StartGrabbing() { if (SelectedCamera == null) return; try { var camera = _cameraManager.GetCamera(SelectedCamera.Id); if (camera == null) return; // 同一时间只允许一路预览:采集另一台相机前先停掉当前这一路, // 否则旧相机的帧订阅仍在,两路画面会交替串进同一个调试页 if (_grabbingCamera != null && !ReferenceEquals(_grabbingCamera, camera)) StopGrabbingInternal(_grabbingCamera); if (!camera.IsConnected) { camera.OpenDevice(); SelectedCamera.IsConnected = camera.IsConnected; } camera.ImageCallbackEvent += OnImageCallback; camera.StartGrabbing(); SelectedCamera.IsGrabbing = true; _grabbingCamera = camera; } catch (Exception ex) { DialogHelper.ShowError("开始采集失败", ex); } } private void StopGrabbing() { var camera = _cameraManager.GetCamera(SelectedCamera?.Id ?? Guid.Empty); StopGrabbingInternal(camera ?? _grabbingCamera); } /// 停掉指定相机的采集并退订帧事件(按 Id 同步列表行的采集状态标记) private void StopGrabbingInternal(ICamera camera) { if (camera == null) return; try { camera.StopGrabbing(); camera.ImageCallbackEvent -= OnImageCallback; var info = Cameras?.FirstOrDefault(c => c.Id == camera.ID); if (info != null) { info.IsGrabbing = false; info.IsConnected = camera.IsConnected; } if (info != null && ReferenceEquals(SelectedCamera, info)) { SelectedCamera.IsGrabbing = false; CurrentImage = null; FpsText = ""; } } catch (Exception ex) { DialogHelper.ShowError("停止采集失败", ex); } finally { if (ReferenceEquals(_grabbingCamera, camera)) _grabbingCamera = null; } } private void OnImageCallback(GenImage image, TimeSpan elapsed, string info) { if (image == null) return; try { var bitmap = image.ToBitmap(); var hBitmap = bitmap.GetHbitmap(); var bmpSource = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); DeleteObject(hBitmap); bitmap.Dispose(); bmpSource.Freeze(); Application.Current?.Dispatcher?.BeginInvoke(new Action(() => { CurrentImage = bmpSource; FpsText = elapsed.TotalMilliseconds > 0 ? $"{1000.0 / elapsed.TotalMilliseconds:F1} fps" : ""; // 把画面推送给当前调试页(若其具备图像接收能力) if (CurrentDebugPage is ICameraImageSink sink) sink.ShowImage(bmpSource); })); } catch { } } #endregion #region 相机列表刷新 & 配置保存 private void RefreshCameraList() { var selectedId = SelectedCamera?.Id ?? Guid.Empty; Cameras.Clear(); var infos = _cameraManager.GetAllCameraInfos(); foreach (var info in infos) { Cameras.Add(info); } // 保持原选中项(重建调试页容器与属性编辑器绑定) if (selectedId != Guid.Empty) { var match = Cameras.FirstOrDefault(c => c.Id == selectedId); if (match != null && !ReferenceEquals(match, SelectedCamera)) SelectedCamera = match; } } private void SaveCameraConfig() { try { if (_cameraManager.SaveConfig()) { RefreshCameraList(); DialogHelper.Success("相机配置已保存"); } else DialogHelper.ShowError("相机配置保存失败,请查看日志"); } catch (Exception ex) { DialogHelper.ShowError("保存相机配置失败", ex); } } #endregion } }