| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- 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 AutoFocusTool.Calib;
- using AutoFocusTool.Imaging;
- namespace AutoFocusTool
- {
- // well选择器 + 手动转well + 一键全自动初始化(勾选well + 独立窗口)
- public partial class MainWindow
- {
- private CancellationTokenSource _calibCts;
- private const int WELL_COUNT = 16;
- private readonly List<CheckBox> _wellChecks = new List<CheckBox>();
- // 业务闭环:转well时优先应用 calibration.json 的标定结果(P0-1)。
- private readonly CalibrationManager _calibMgr = new CalibrationManager();
- /// <summary>连接成功后填充:手动well下拉(1-16) + 16个勾选框。</summary>
- private void InitWellSelectors()
- {
- // 手动控制的well下拉
- CmbWell.Items.Clear();
- for (int i = 1; i <= WELL_COUNT; i++) CmbWell.Items.Add($"well {i}");
- CmbWell.SelectedIndex = 0;
- // 自动标定的16个勾选框(默认全选)
- if (_wellChecks.Count == 0)
- {
- WellCheckPanel.Children.Clear();
- for (int i = 1; i <= WELL_COUNT; i++)
- {
- var cb = new CheckBox
- {
- Content = i.ToString(),
- IsChecked = true,
- Foreground = Brushes.White,
- Width = 46,
- Margin = new Thickness(2)
- };
- _wellChecks.Add(cb);
- WellCheckPanel.Children.Add(cb);
- }
- }
- }
- // ── 需求1:手动转到选定well ──
- // P0-1 业务闭环:优先应用 calibration.json 的标定参数(水平/Z/曝光),
- // 标定合格(检到圆且峰比>1.2)才用JSON,否则降级到EEPROM。
- private void BtnGoWell_Click(object sender, RoutedEventArgs e)
- {
- LogAction($"点击 转到well={CmbWell.SelectedIndex + 1}");
- if (_motor == null) { Log("未连接。"); return; }
- if (_busy) { Log("设备忙。"); return; }
- int well = CmbWell.SelectedIndex + 1;
- int houseSn = _current?.HouseSn ?? 0;
- int motorDelay = ParseInt(TxtMotorDelay.Text, 1500);
- Task.Run(() =>
- {
- _busy = true;
- try
- {
- _motor.MotorDelayMs = motorDelay;
- bool useCalib = _calibMgr.HasValidCalibration(houseSn, well);
- var (hpos, zpos, exp) = _calibMgr.GetWellParams(houseSn, well, _motor);
- if (hpos < 0) { Log($"读well{well}位置失败"); return; }
- _motor.HorizontalMoveTo(hpos);
- if (zpos > 0) _motor.VerticalMoveTo(zpos);
- if (useCalib && exp > 0 && _camera != null) _camera.SetExposure(exp);
- _curWell = well;
- string src = useCalib ? "标定结果" : "EEPROM(未标定/不合格)";
- string expInfo = (useCalib && exp > 0) ? $" 曝光={exp}" : "";
- Log($"已转到 well{well}[{src}]: 水平={hpos} Z={zpos}{expInfo}");
- Dispatcher.Invoke(() =>
- {
- TxtWellInfo.Text = $"well{well}[{src}]: 水平={hpos} Z={zpos}{expInfo}";
- UpdateParamDisplay();
- });
- }
- catch (Exception ex) { Log($"转well异常: {ex.Message}"); }
- finally { _busy = false; }
- RefreshPositions();
- });
- }
- // ── 需求2:全选/全不选 ──
- private void BtnWellAll_Click(object sender, RoutedEventArgs e)
- { LogAction("点击 全选well"); foreach (var cb in _wellChecks) cb.IsChecked = true; }
- private void BtnWellNone_Click(object sender, RoutedEventArgs e)
- { LogAction("点击 全不选well"); foreach (var cb in _wellChecks) cb.IsChecked = false; }
- // ── 需求2、3:一键全自动初始化(勾选well + 独立窗口显示)──
- private void BtnAutoInit_Click(object sender, RoutedEventArgs e)
- {
- LogAction("点击 一键全自动初始化");
- if (_camera == null || _motor == null) { Log("需要先连接相机+串口。"); return; }
- if (_busy) { Log("设备忙,请稍候。"); return; }
- // 收集勾选的well
- var wells = new List<int>();
- for (int i = 0; i < _wellChecks.Count; i++)
- if (_wellChecks[i].IsChecked == true) wells.Add(i + 1);
- if (wells.Count == 0) { Log("请至少勾选一个well。"); return; }
- int motorDelay = ParseInt(TxtMotorDelay.Text, 1500);
- int houseSn = _current?.HouseSn ?? 0;
- string port = _current?.PortName ?? "";
- int ccdIdx = _current?.CcdIndex ?? -1;
- string ccdSn = _current?.CcdSn ?? "";
- // 弹出标定窗口
- var win = new CalibWindow { Owner = this };
- _calibCts = new CancellationTokenSource();
- var token = _calibCts.Token;
- win.StopRequested += () => _calibCts?.Cancel();
- win.Show();
- win.SetProgress("开始自动标定", $"舱{houseSn},共 {wells.Count} 个well: {string.Join(",", wells)}");
- Task.Run(() =>
- {
- _busy = true;
- try
- {
- _motor.MotorDelayMs = motorDelay;
- _motor.OpenLED();
- Thread.Sleep(200);
- var engine = new CalibrationEngine(_motor, _camera)
- {
- Log = Log,
- OnFrame = buf =>
- {
- try
- {
- var bmp = ImageConverter.ToBitmapSource(buf, _camWidth, _camHeight);
- Dispatcher.BeginInvoke((Action)(() => { ImgPreview.Source = bmp; win.SetCurrentFrame(bmp); }));
- }
- catch { }
- },
- OnStep = (msg, circle, exp) =>
- {
- string circlePart = circle != null && circle.Found
- ? $"圆心偏移 Y:{circle.OffsetYPct:F0}% X:{circle.OffsetXPct:F0}% 完整:{circle.Complete}"
- : "未检出well圆";
- string expPart = exp != null ? $" 曝光信息:{exp}" : "";
- string param = $"Z:{_lastZ} 水平:{_lastH} {circlePart}{expPart}";
- win.SetCurrentInfo(param, msg);
- AutoFocusTool.Logging.FileLogger.Data("CALIB", $"{msg} | {param}");
- }
- };
- var houseCalib = new HouseCalib { House = houseSn, Port = port, CcdIndex = ccdIdx, CcdSn = ccdSn };
- string resultDir = @"C:\claudeFile\TL\AutoFocusTool\calib_result";
- System.IO.Directory.CreateDirectory(resultDir);
- int done = 0;
- foreach (int well in wells)
- {
- if (token.IsCancellationRequested) { Log("标定已停止。"); break; }
- done++;
- win.SetProgress($"正在标定 well {well}", $"进度 {done}/{wells.Count}");
- win.MarkActive(well);
- int hpos = _motor.ReadWellHorizontalPos(well);
- int zZero = _motor.ReadWellFocusZero(well);
- Log($"\n=== well{well} EEPROM: 水平={hpos} Z零点={zZero} ===");
- if (hpos < 0) { Log($"well{well} 读位置失败,跳过"); continue; }
- if (zZero < 0) zZero = 0;
- var wc = engine.CalibrateWell(well, hpos, zZero);
- houseCalib.Wells.Add(wc);
- // 存终图 + 在窗口对应格显示
- try
- {
- _camera.SetExposure(wc.Exposure);
- _motor.HorizontalMoveTo(wc.HorizontalPulse);
- _motor.VerticalMoveTo(wc.FocusZ);
- Thread.Sleep(150);
- _camera.GrabRgb();
- var buf = _camera.GetSourceBuffer();
- string imgPath = $"{resultDir}\\house{houseSn}_well{well}_标定后.bmp";
- ImageConverter.SaveBmp(buf, _camWidth, _camHeight, imgPath);
- var bmp = ImageConverter.ToBitmapSource(buf, _camWidth, _camHeight);
- bool ok = Math.Abs(wc.CenterOffsetPct) < 12 && wc.CircleFound && wc.PeakRatio > 1.2;
- win.SetWellResult(well, bmp,
- $"well{well} Y偏{wc.CenterOffsetPct:F0}% 曝{wc.Exposure} Z{wc.FocusZ}", ok);
- Log($" well{well} 标定后画面已存: {imgPath}");
- }
- catch (Exception ex) { Log($" 存图失败: {ex.Message}"); }
- }
- _motor.CloseLED();
- // 存档
- string path = @"C:\claudeFile\TL\AutoFocusTool\calibration.json";
- var file = new CalibrationFile
- {
- TlSn = $"house{houseSn}",
- Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm")
- };
- file.Houses.Add(houseCalib);
- file.Save(path);
- _calibMgr.RefreshCache(); // P0-1: 重写JSON后刷新缓存,转well立即用新结果
- Log($"=== 标定完成,已存 {path} ===");
- win.SetProgress("标定完成 ✓", $"已标定 {houseCalib.Wells.Count} 个well,结果已保存");
- }
- catch (Exception ex) { Log($"标定异常: {ex.Message}"); win.SetProgress("标定异常", ex.Message); }
- finally { _busy = false; RefreshPositions(); }
- }, token);
- }
- }
- }
|