using System; using System.Linq; using System.Threading; using System.Collections.Generic; using AutoFocusTool.Devices; using AutoFocusTool.Serial; using AutoFocusTool.Imaging; using AutoFocusTool.Calib; using SerialCamera = AutoFocusTool.Camera.Camera; namespace AutoFocusTool { /// /// 自动标定(连机):用 CalibrationEngine 对舱室的若干well做 /// 居中→曝光→对焦,结果写 calibration.json。 /// 用法:Calibrate.exe [houseSn=9] [wellFrom=1] [wellTo=2] /// internal class Calibrate { const int W = 2592, H = 1944; // MVC2000原生分辨率(参考图实测2592x1944),1600x1200会裁成中央块 const string OutDir = @"C:\claudeFile\TL\AutoFocusTool\calib"; const string JsonPath = @"C:\claudeFile\TL\AutoFocusTool\calibration.json"; static void Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}"); System.IO.Directory.CreateDirectory(OutDir); int houseSn = args.Length > 0 && int.TryParse(args[0], out int hs) ? hs : 9; int wFrom = args.Length > 1 && int.TryParse(args[1], out int a) ? a : 1; int wTo = args.Length > 2 && int.TryParse(args[2], out int b) ? b : 2; L($"==== 自动标定 舱{houseSn} well{wFrom}~{wTo} ===="); var scanner = new DeviceScanner { Log = _ => { } }; var houses = scanner.ScanAll(); var h = houses.FirstOrDefault(x => x.HouseSn == houseSn && x.HasCamera); if (h == null) { L("未找到该舱或无相机"); return; } using var motor = new HouseMotor(h.PortName) { Log = _ => { }, MotorDelayMs = 1500 }; motor.Open(); using var cam = new SerialCamera(h.CcdIndex, W, H, 120); if (cam.Init() != 0) { L("相机初始化失败"); return; } cam.SetOpMode(0); motor.OpenLED(); Thread.Sleep(300); var engine = new CalibrationEngine(motor, cam) { Log = L, OnFrame = b => { /* 命令行下不显示,GUI会用 */ }, DebugSave = (b, name) => { try { Diag.SaveBmp(b, W, H, $@"C:\claudeFile\TL\AutoFocusTool\hscan\{name}.bmp"); } catch { } }, }; var houseCalib = new HouseCalib { House = houseSn, Port = h.PortName, CcdIndex = h.CcdIndex, CcdSn = h.CcdSn }; int saveCounter = 0; for (int well = wFrom; well <= wTo; well++) { int hpos = motor.ReadWellHorizontalPos(well); int zZero = motor.ReadWellFocusZero(well); L($"\n── well{well} EEPROM: 水平={hpos} Z零点={zZero} ──"); if (hpos < 0) { L("读well水平位置失败,跳过"); continue; } if (zZero < 0) zZero = 0; // 存关键帧:标定完well后存最终图 engine.OnFrame = bb => { }; var wc = engine.CalibrateWell(well, hpos, zZero); houseCalib.Wells.Add(wc); // 存最清晰层终图 cam.SetExposure(wc.Exposure); motor.HorizontalMoveTo(wc.HorizontalPulse); motor.VerticalMoveTo(wc.FocusZ); Thread.Sleep(150); cam.GrabRgb(); Diag.SaveBmp(cam.GetSourceBuffer(), W, H, $"{OutDir}\\cal_h{houseSn}_w{well}_FINAL.bmp"); saveCounter++; } motor.CloseLED(); // 写档案(合并已有) var file = new CalibrationFile { TlSn = $"house{houseSn}", Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm") }; file.Houses.Add(houseCalib); file.Save(JsonPath); L($"\n==== 标定完成,写入 {JsonPath} ===="); foreach (var w in houseCalib.Wells) L($" well{w.Well}: 水平={w.HorizontalPulse} 曝光={w.Exposure} 对焦Z={w.FocusZ} 偏移={w.CenterOffsetPct:F1}% 峰比={w.PeakRatio:F2} {w.Note}"); } } }