using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using AutoFocusTool.Devices; using AutoFocusTool.Imaging; using AutoFocusTool.Serial; using SerialCamera = AutoFocusTool.Camera.Camera; namespace AutoFocusTool { public partial class MainWindow : Window { private readonly DeviceScanner _scanner = new DeviceScanner(); private List _houses = new List(); private SerialCamera _camera; // 当前连接的相机 private HouseMotor _motor; // 当前连接的串口马达+光源 private HouseDevice _current; // 当前舱室 private CancellationTokenSource _liveCts; // 实时预览 private CancellationTokenSource _scanCts; // Z扫描 private volatile bool _busy; // 串口忙(避免并发命令) private int _lastZ = -1, _lastH = -1, _curWell = -1; // 最近回读的Z/水平位置、当前well(供参数显示) private int _camWidth = 2592, _camHeight = 1944; // MVC2000 原生分辨率(参考图实测),勿用1600x1200否则画面被裁中央 private List<(int z, double score)> _curve = new List<(int, double)>(); public MainWindow() { InitializeComponent(); AutoFocusTool.Logging.FileLogger.Init(); _scanner.Log = Log; Closing += (s, e) => { AutoFocusTool.Logging.FileLogger.Info("APP", "程序关闭"); Cleanup(); }; Log("程序启动。先【扫描设备】,选舱室后【连接】。"); } // ───────────────────────── 日志 ───────────────────────── private void Log(string msg) { AutoFocusTool.Logging.FileLogger.Info("RUN", msg); void Append() { string line = $"{DateTime.Now:HH:mm:ss} {msg}\n"; TxtLog.AppendText(line); TxtLog.ScrollToEnd(); } if (Dispatcher.CheckAccess()) Append(); else Dispatcher.BeginInvoke((Action)Append); } /// 记录界面操作(ACTION级落盘 + 界面日志)。 private void LogAction(string msg) { AutoFocusTool.Logging.FileLogger.Action("UI", msg); void Append() { string line = $"{DateTime.Now:HH:mm:ss} ▶ {msg}\n"; TxtLog.AppendText(line); TxtLog.ScrollToEnd(); } if (Dispatcher.CheckAccess()) Append(); else Dispatcher.BeginInvoke((Action)Append); } // ───────────────────────── 扫描设备 ───────────────────────── private async void BtnScan_Click(object sender, RoutedEventArgs e) { LogAction("点击 扫描设备"); BtnScan.IsEnabled = false; Log("开始扫描设备(枚举相机 + 扫串口握手)..."); try { var houses = await Task.Run(() => _scanner.ScanAll()); _houses = houses; CmbHouse.Items.Clear(); foreach (var h in houses) CmbHouse.Items.Add(h.ToString()); if (houses.Count > 0) { CmbHouse.SelectedIndex = 0; Log($"扫描完成,发现 {houses.Count} 个舱室。"); } else { Log("未发现舱室。检查串口连接/相机USB/驱动。"); } } catch (Exception ex) { Log($"扫描异常: {ex.Message}"); } finally { BtnScan.IsEnabled = true; } } // ───────────────────────── 连接 / 断开 ───────────────────────── private void BtnConnect_Click(object sender, RoutedEventArgs e) { LogAction($"点击 连接 舱室索引={CmbHouse.SelectedIndex}"); int idx = CmbHouse.SelectedIndex; if (idx < 0 || idx >= _houses.Count) { Log("请先扫描并选择一个舱室。"); return; } var h = _houses[idx]; try { // 串口 _motor = new HouseMotor(h.PortName) { Log = Log }; if (!_motor.Open()) { Log($"串口 {h.PortName} 打开失败。"); _motor = null; return; } _motor.MotorDelayMs = ParseInt(TxtMotorDelay.Text, 1500); // 相机(若配对到) if (h.HasCamera) { _camera = new SerialCamera(h.CcdIndex, _camWidth, _camHeight, ParseInt(TxtExposure.Text, 400)); int init = _camera.Init( (byte)ParseInt(TxtGainR.Text, 25), (byte)ParseInt(TxtGainG.Text, 14), (byte)ParseInt(TxtGainB.Text, 25)); if (init == 0) { _camera.SetOpMode(0); // 拍照模式 Log($"相机#{h.CcdIndex} 已连接。"); } else { Log($"相机#{h.CcdIndex} 初始化失败(code={init}),仅串口可用。"); _camera.Dispose(); _camera = null; } } else { Log("该舱室未配对相机,仅串口可用。"); } _current = h; TxtConnState.Text = $"已连接 舱室{h.HouseSn}"; TxtConnState.Foreground = Brushes.LightGreen; ControlPanel.IsEnabled = true; BtnConnect.IsEnabled = false; BtnDisconnect.IsEnabled = true; BtnScan.IsEnabled = false; InitWellSelectors(); // 填充well下拉 + 16个勾选框 RefreshPositions(); } catch (Exception ex) { Log($"连接异常: {ex.Message}"); } } private void BtnDisconnect_Click(object sender, RoutedEventArgs e) { LogAction("点击 断开"); Cleanup(); TxtConnState.Text = "未连接"; TxtConnState.Foreground = Brushes.Salmon; ControlPanel.IsEnabled = false; BtnConnect.IsEnabled = true; BtnDisconnect.IsEnabled = false; BtnScan.IsEnabled = true; Log("已断开。"); } private void Cleanup() { try { _liveCts?.Cancel(); } catch { } try { _scanCts?.Cancel(); } catch { } try { if (_motor != null && _camera != null) _motor.CloseLED(); } catch { } try { _camera?.Dispose(); } catch { } try { _motor?.Dispose(); } catch { } _camera = null; _motor = null; _current = null; } private static int ParseInt(string s, int def) => int.TryParse(s?.Trim(), out int v) ? v : def; } }