Procházet zdrojové kódy

chore: 清理测试工具与子工程,只留主程序+文档

- 删除原有命令行子工程 Calibrate/CalibTest/SelfTest/SmokeTest(测试/标定工具,主程序不依赖)
- 清理csproj失效排除行(只留bin/obj)
- 本次会话临时测试遗留(GainTest/Logs/测试图)已删
- 保留:主程序(WPF)、相机自动对焦项目-总方案.md、自动对焦流程图.html

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie před 1 týdnem
rodič
revize
1e36b67bb7

+ 1 - 14
AutoFocusTool.csproj

@@ -14,22 +14,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <!-- 排除独立exe子工程目录(各自单独编译),避免其obj/源文件被主工程重复编译 -->
-    <Compile Remove="SelfTest\**\*.cs" />
-    <Compile Remove="Survey\**\*.cs" />
-    <Compile Remove="SmokeTest\**\*.cs" />
-    <Compile Remove="CalibTest\**\*.cs" />
-    <Compile Remove="Calibrate\**\*.cs" />
-    <Compile Remove="HScan\**\*.cs" />
+    <!-- 排除编译输出目录,避免其源文件被主工程重复编译 -->
     <Compile Remove="bin\**\*.cs" />
     <Compile Remove="obj\**\*.cs" />
-    <Page Remove="SelfTest\**" />
-    <Page Remove="Survey\**" />
-    <Page Remove="SmokeTest\**" />
-    <Page Remove="CalibTest\**" />
-    <Page Remove="Calibrate\**" />
-    <Page Remove="HScan\**" />
-    <Page Remove="ToPng\**" />
   </ItemGroup>
 
   <ItemGroup>

+ 0 - 77
CalibTest/CalibTest.cs

@@ -1,77 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using AutoFocusTool.Imaging;
-
-namespace AutoFocusTool
-{
-    /// <summary>
-    /// 离线验证标定算法:读已存的BMP → WellDetector找圆心 → ExposureMeter评曝光。
-    /// 不连机器,快速验证算法对真实图是否有效。
-    /// 用法:CalibTest.exe [目录或文件glob]
-    /// </summary>
-    internal class CalibTest
-    {
-        static void Main(string[] args)
-        {
-            Console.OutputEncoding = System.Text.Encoding.UTF8;
-            string dir = args.Length > 0 ? args[0] : @"C:\claudeFile\TL\AutoFocusTool\survey";
-            string pat = args.Length > 1 ? args[1] : "h9_w2_*.bmp";
-
-            var files = Directory.GetFiles(dir, pat).OrderBy(f => f).ToList();
-            Console.WriteLine($"== 离线标定算法验证: {files.Count} 张图 ({pat}) ==");
-            Console.WriteLine($"{"文件",-22} {"圆心X",6} {"圆心Y",6} {"半径",6} {"偏移X%",7} {"偏移Y%",7} {"完整",5} {"曝光均值",8} {"饱和%",6} {"曝光判定",8} {"清晰度",8}");
-
-            foreach (var f in files)
-            {
-                var (buf, w, h) = ReadBmp24(f);
-                if (buf == null) { Console.WriteLine($"{Path.GetFileName(f)}: 读取失败"); continue; }
-
-                var circle = WellDetector.Detect(buf, w, h);
-                var exp = ExposureMeter.Measure(buf, w, h, circle);
-                double sharp = Sharpness.Compute(buf, w, h,
-                    circle.Found ? RoiOf(circle, w, h) : (System.Drawing.Rectangle?)null);
-
-                string name = Path.GetFileNameWithoutExtension(f);
-                if (circle.Found)
-                    Console.WriteLine($"{name,-22} {circle.Cx,6:F0} {circle.Cy,6:F0} {circle.Radius,6:F0} {circle.OffsetXPct,7:F1} {circle.OffsetYPct,7:F1} {(circle.Complete ? "是" : "否"),5} {exp.Mean,8:F0} {exp.SaturatedPct,6:F1} {exp.State,8} {sharp,8:F4}");
-                else
-                    Console.WriteLine($"{name,-22} 未检出well圆  曝光均值={exp.Mean:F0}  拒绝原因[{circle.RejectReason}] 面积={circle.AreaPct:F1}%");
-            }
-        }
-
-        // well圆内ROI(内部70%)做清晰度
-        static System.Drawing.Rectangle RoiOf(WellCircle c, int w, int h)
-        {
-            int r = (int)(c.Radius * 0.7);
-            int x = Math.Max(0, (int)c.Cx - r), y = Math.Max(0, (int)c.Cy - r);
-            int ww = Math.Min(2 * r, w - x), hh = Math.Min(2 * r, h - y);
-            return new System.Drawing.Rectangle(x, y, ww, hh);
-        }
-
-        /// <summary>读24bpp BMP → (BGR字节[自顶向下], 宽, 高)。我们存图时h>0自底向上,这里翻回自顶向下。</summary>
-        static (byte[], int, int) ReadBmp24(string path)
-        {
-            try
-            {
-                byte[] all = File.ReadAllBytes(path);
-                if (all.Length < 54 || all[0] != 'B' || all[1] != 'M') return (null, 0, 0);
-                int off = BitConverter.ToInt32(all, 10);
-                int w = BitConverter.ToInt32(all, 18);
-                int h = BitConverter.ToInt32(all, 22);
-                short bpp = BitConverter.ToInt16(all, 28);
-                if (bpp != 24) return (null, 0, 0);
-                bool bottomUp = h > 0; h = Math.Abs(h);
-                int rowSize = ((w * 3 + 3) / 4) * 4;
-                var buf = new byte[w * h * 3];
-                for (int y = 0; y < h; y++)
-                {
-                    int srcRow = off + (bottomUp ? (h - 1 - y) : y) * rowSize;
-                    Buffer.BlockCopy(all, srcRow, buf, y * w * 3, w * 3);
-                }
-                return (buf, w, h);
-            }
-            catch { return (null, 0, 0); }
-        }
-    }
-}

+ 0 - 22
CalibTest/CalibTest.csproj

@@ -1,22 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0-windows</TargetFramework>
-    <Nullable>disable</Nullable>
-    <ImplicitUsings>disable</ImplicitUsings>
-    <Platforms>x64</Platforms>
-    <PlatformTarget>x64</PlatformTarget>
-    <AssemblyName>CalibTest</AssemblyName>
-    <RootNamespace>AutoFocusTool</RootNamespace>
-    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Compile Include="..\Imaging\Sharpness.cs" />
-    <Compile Include="..\Imaging\WellDetector.cs" />
-    <Compile Include="..\Imaging\ExposureMeter.cs" />
-    <Compile Include="CalibTest.cs" />
-  </ItemGroup>
-
-</Project>

+ 0 - 90
Calibrate/Calibrate.cs

@@ -1,90 +0,0 @@
-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
-{
-    /// <summary>
-    /// 自动标定(连机):用 CalibrationEngine 对舱室的若干well做
-    /// 居中→曝光→对焦,结果写 calibration.json。
-    /// 用法:Calibrate.exe [houseSn=9] [wellFrom=1] [wellTo=2]
-    /// </summary>
-    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}");
-        }
-    }
-}

+ 0 - 44
Calibrate/Calibrate.csproj

@@ -1,44 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0-windows</TargetFramework>
-    <Nullable>disable</Nullable>
-    <ImplicitUsings>disable</ImplicitUsings>
-    <Platforms>x64</Platforms>
-    <PlatformTarget>x64</PlatformTarget>
-    <AssemblyName>Calibrate</AssemblyName>
-    <RootNamespace>AutoFocusTool</RootNamespace>
-    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="System.IO.Ports" Version="8.0.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\Camera\MVCAPI.cs" />
-    <Compile Include="..\Camera\CapInfoStruct.cs" />
-    <Compile Include="..\Camera\Camera.cs" />
-    <Compile Include="..\Serial\Protocol.cs" />
-    <Compile Include="..\Serial\SerialMotor.cs" />
-    <Compile Include="..\Serial\HouseMotor.cs" />
-    <Compile Include="..\Devices\HouseDevice.cs" />
-    <Compile Include="..\Devices\DeviceScanner.cs" />
-    <Compile Include="..\Imaging\Sharpness.cs" />
-    <Compile Include="..\Imaging\WellDetector.cs" />
-    <Compile Include="..\Imaging\ExposureMeter.cs" />
-    <Compile Include="..\Calib\CalibrationFile.cs" />
-    <Compile Include="..\Calib\CalibrationEngine.cs" />
-    <Compile Include="..\SelfTest\Diag.cs" />
-    <Compile Include="Calibrate.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\DependFile\ccd\*.dll">
-      <Link>DependFile\ccd\%(Filename)%(Extension)</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-
-</Project>

+ 0 - 55
SelfTest/Diag.cs

@@ -1,55 +0,0 @@
-using System;
-using System.IO;
-
-namespace AutoFocusTool
-{
-    /// <summary>
-    /// 自检用轻量图像诊断(不依赖 WPF)。
-    /// 统计像素亮度,判断是全黑/全白/有图;裸写 24bpp BMP 存盘供肉眼确认。
-    /// </summary>
-    internal static class Diag
-    {
-        /// <summary>返回 (最小, 最大, 平均) 灰度。</summary>
-        public static (int min, int max, double mean) Stats(byte[] bgr24)
-        {
-            int min = 255, max = 0;
-            long sum = 0;
-            // 每3字节取灰度,步进采样(每隔9像素)加速
-            int n = 0;
-            for (int i = 0; i + 2 < bgr24.Length; i += 27)
-            {
-                int g = (bgr24[i] * 29 + bgr24[i + 1] * 150 + bgr24[i + 2] * 77) >> 8;
-                if (g < min) min = g;
-                if (g > max) max = g;
-                sum += g; n++;
-            }
-            return (min, max, n > 0 ? (double)sum / n : 0);
-        }
-
-        /// <summary>裸写 24bpp BMP(含 FlipY 翻正)。</summary>
-        public static void SaveBmp(byte[] bgr24, int w, int h, string path)
-        {
-            int rowSize = ((w * 3 + 3) / 4) * 4; // 4字节对齐
-            int imgSize = rowSize * h;
-            int fileSize = 54 + imgSize;
-            using var fs = new FileStream(path, FileMode.Create);
-            using var bw = new BinaryWriter(fs);
-            // BMP 文件头 14
-            bw.Write((byte)'B'); bw.Write((byte)'M');
-            bw.Write(fileSize); bw.Write(0); bw.Write(54);
-            // DIB 头 40
-            bw.Write(40); bw.Write(w); bw.Write(h); // h>0 → 自底向上,正好翻正相机的倒像
-            bw.Write((short)1); bw.Write((short)24);
-            bw.Write(0); bw.Write(imgSize);
-            bw.Write(2835); bw.Write(2835); bw.Write(0); bw.Write(0);
-            // 像素:BMP 自底向上存,相机数据是倒的,直接顺序写即翻正
-            byte[] pad = new byte[rowSize - w * 3];
-            for (int y = 0; y < h; y++)
-            {
-                int row = y * w * 3;
-                bw.Write(bgr24, row, w * 3);
-                if (pad.Length > 0) bw.Write(pad);
-            }
-        }
-    }
-}

+ 0 - 134
SelfTest/Program.cs

@@ -1,134 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using AutoFocusTool.Devices;
-using AutoFocusTool.Serial;
-using AutoFocusTool.Imaging;
-using AutoFocusTool.Calib;
-using SerialCamera = AutoFocusTool.Camera.Camera;
-
-namespace AutoFocusTool
-{
-    /// <summary>
-    /// 命令行硬件自检:不开GUI,直接联调 光源+电机+相机。
-    /// 流程:扫描设备 → 选舱室 → 连接 → 开光源 → Z序列扫描(逐层抓帧算清晰度) → 选层 → 关光源。
-    /// 用法:SelfTest.exe [houseSn]   不带参数则测第一个发现的舱室。
-    /// </summary>
-    internal class Program
-    {
-        static int Main(string[] args)
-        {
-            Console.OutputEncoding = System.Text.Encoding.UTF8;
-            void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}");
-
-            L("========== 相机自动对焦 硬件自检 ==========");
-            int wantHouse = (args.Length > 0 && int.TryParse(args[0], out int hs)) ? hs : -1;
-            int wantExp = (args.Length > 1 && int.TryParse(args[1], out int expArg)) ? expArg : 400;
-
-            var scanner = new DeviceScanner { Log = L };
-
-            // 1) 扫描
-            L("【1】扫描设备(枚举相机 + 扫串口握手)...");
-            var houses = scanner.ScanAll();
-            if (houses.Count == 0)
-            {
-                L("✗ 未发现任何舱室。检查:串口线、相机USB、相机驱动、培养箱电源。");
-                return 1;
-            }
-            L($"发现 {houses.Count} 个舱室:");
-            foreach (var h in houses) L($"    {h}");
-
-            // 2) 选舱室
-            HouseDevice target = wantHouse > 0
-                ? houses.FirstOrDefault(x => x.HouseSn == wantHouse)
-                : houses.FirstOrDefault(x => x.HasCamera) ?? houses[0];
-            if (target == null) { L($"✗ 未找到舱室 {wantHouse}"); return 1; }
-            L($"【2】选定 {target}");
-
-            // 3) 连接串口
-            L("【3】连接串口...");
-            using var motor = new HouseMotor(target.PortName) { Log = L, MotorDelayMs = 1500 };
-            if (!motor.Open()) { L("✗ 串口打开失败"); return 1; }
-            int sn = motor.ShakeHands();
-            L($"    握手回报 houseSn={sn}");
-            int vpos = motor.ReadVerticalPosition();
-            int hpos = motor.ReadHorizontalPosition();
-            L($"    当前位置 Z={vpos} 水平={hpos}");
-
-            // 4) 连接相机
-            SerialCamera cam = null;
-            if (target.HasCamera)
-            {
-                L($"【4】连接相机#{target.CcdIndex}...");
-                cam = new SerialCamera(target.CcdIndex, 1600, 1200, wantExp);
-                int init = cam.Init();
-                if (init == 0) { cam.SetOpMode(0); cam.SetExposure(wantExp); L($"    相机就绪(拍照模式,曝光={wantExp}×100µs={wantExp/10.0}ms)"); }
-                else { L($"✗ 相机初始化失败 code={init}"); cam.Dispose(); cam = null; }
-            }
-            else L("【4】该舱无配对相机,跳过相机测试");
-
-            // 5) 开光源
-            L("【5】开光源 LED...");
-            bool led = motor.OpenLED();
-            L($"    开光源 {(led ? "OK" : "失败")}");
-            Thread.Sleep(300);
-
-            // 6) Z序列扫描 + 算清晰度
-            if (cam != null)
-            {
-                L("【6】Z序列扫描...");
-                L("    先 Z 复位归零...");
-                motor.VerticalReset();
-                int start = 0;
-                int step = (args.Length > 2 && int.TryParse(args[2], out int st)) ? st : 128;
-                int count = (args.Length > 3 && int.TryParse(args[3], out int ct)) ? ct : 11;
-                L($"    层距={step}脉冲 层数={count} 覆盖Z=0~{step*(count-1)}");
-                var roi = new System.Drawing.Rectangle(400, 300, 800, 600); // 中心ROI
-                var results = new System.Collections.Generic.List<(int z, double score)>();
-
-                for (int i = 0; i < count; i++)
-                {
-                    int z = start + step * i;
-                    motor.VerticalMoveTo(z);          // 移动+等motorDelay稳定
-                    int g = cam.GrabRgb();
-                    if (g != 0) { L($"    层{i + 1}: 抓帧失败 code={g}"); continue; }
-                    byte[] buf = cam.GetSourceBuffer();
-                    var (mn, mx, avg) = Diag.Stats(buf);
-                    double score = Sharpness.Compute(buf, 1600, 1200, roi);
-                    double scoreFull = Sharpness.Compute(buf, 1600, 1200, null);
-                    results.Add((z, score));
-                    L($"    层{i + 1,2}/{count}: Z={z,-7} ROI分={score:F4} 全图分={scoreFull:F4}  像素[min={mn} max={mx} 均值={avg:F0}]");
-                    // 存前3层图供肉眼确认相机是否真出图
-                    if (i < 3)
-                    {
-                        string p = $"C:\\claudeFile\\TL\\AutoFocusTool\\selftest_house{target.HouseSn}_layer{i + 1}.bmp";
-                        try { Diag.SaveBmp(buf, 1600, 1200, p); L($"        已存图: {p}"); }
-                        catch (Exception ex) { L($"        存图失败: {ex.Message}"); }
-                    }
-                }
-
-                if (results.Count > 0)
-                {
-                    double max = results.Max(r => r.score);
-                    double mean = results.Average(r => r.score);
-                    int pk = results.FindIndex(r => r.score == max);
-                    double ratio = mean > 1e-6 ? max / mean : 1;
-                    L($"【7】选层结果:最清晰=第{pk + 1}层 Z={results[pk].z} 分={max:F1}  max/mean={ratio:F2}");
-                    if (ratio < 1.15) L("    ⚠ 曲线偏平,可能全糊/空孔(需扩范围或确认)");
-                    else L("    ✓ 有明显焦点峰");
-                    // 回到最清晰层
-                    motor.VerticalMoveTo(results[pk].z);
-                    L($"    已移动到最清晰层 Z={results[pk].z}");
-                }
-            }
-
-            // 8) 关光源 + 收尾
-            L("【8】关光源 LED...");
-            motor.CloseLED();
-            cam?.Dispose();
-            L("========== 自检结束 ==========");
-            L("结论:上面每一【步】都有OK即表示 光源+电机+相机 通路正常。");
-            return 0;
-        }
-    }
-}

+ 0 - 44
SelfTest/SelfTest.csproj

@@ -1,44 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0-windows</TargetFramework>
-    <Nullable>disable</Nullable>
-    <ImplicitUsings>disable</ImplicitUsings>
-    <Platforms>x64</Platforms>
-    <PlatformTarget>x64</PlatformTarget>
-    <AssemblyName>SelfTest</AssemblyName>
-    <RootNamespace>AutoFocusTool</RootNamespace>
-    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="System.IO.Ports" Version="8.0.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <!-- 链接主工程的硬件层源文件(不含 WPF 依赖的 ImageConverter) -->
-    <Compile Include="..\Camera\MVCAPI.cs" />
-    <Compile Include="..\Camera\CapInfoStruct.cs" />
-    <Compile Include="..\Camera\Camera.cs" />
-    <Compile Include="..\Serial\Protocol.cs" />
-    <Compile Include="..\Serial\SerialMotor.cs" />
-    <Compile Include="..\Serial\HouseMotor.cs" />
-    <Compile Include="..\Devices\HouseDevice.cs" />
-    <Compile Include="..\Devices\DeviceScanner.cs" />
-    <Compile Include="..\Imaging\Sharpness.cs" />
-    <Compile Include="..\Calib\CalibrationFile.cs" />
-    <Compile Include="..\Calib\CalibrationManager.cs" />
-    <Compile Include="Diag.cs" />
-    <Compile Include="Program.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <!-- 相机原生 DLL 复制到输出目录 -->
-    <None Include="..\DependFile\ccd\*.dll">
-      <Link>DependFile\ccd\%(Filename)%(Extension)</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-
-</Project>

+ 0 - 0
SelfTest/topng.csx


+ 0 - 161
SmokeTest/SmokeTest.cs

@@ -1,161 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using AutoFocusTool.Devices;
-using AutoFocusTool.Serial;
-using AutoFocusTool.Imaging;
-using AutoFocusTool.Calib;
-using SerialCamera = AutoFocusTool.Camera.Camera;
-
-namespace AutoFocusTool
-{
-    /// <summary>
-    /// 真机冒烟测试:验证第二轮修复,不跑完整标定(不大幅驱动电机)。
-    /// 用法:SmokeTest.exe [testHouse=1]   1号舱=空皿测试舱,9号舱=有皿(已标定)
-    /// 验证项:
-    ///   1) 设备扫描 + COM/相机映射
-    ///   2) P0-1: CalibrationManager 对已标定舱返回JSON参数;未标定降级EEPROM
-    ///   3) P0-2/3: 设两档曝光,确认"丢帧后第2帧"亮度跟随曝光(旧帧污染会导致不跟随)
-    ///   4) 相机抓帧通路
-    /// </summary>
-    internal class SmokeTest
-    {
-        const int W = 2592, H = 1944;
-        const string JsonPath = @"C:\claudeFile\TL\AutoFocusTool\calibration.json";
-        static int pass = 0, fail = 0;
-
-        static void Ok(string m)   { Console.WriteLine($"  [PASS] {m}"); pass++; }
-        static void No(string m)   { Console.WriteLine($"  [FAIL] {m}"); fail++; }
-        static void Info(string m) { Console.WriteLine($"  · {m}"); }
-
-        static void Main(string[] args)
-        {
-            Console.OutputEncoding = System.Text.Encoding.UTF8;
-            int testHouse = args.Length > 0 && int.TryParse(args[0], out int t) ? t : 1;
-            Console.WriteLine($"==== 真机冒烟测试 (测试舱={testHouse}, 空皿用1号舱) ====\n");
-
-            // ── 1) 设备扫描 ──
-            Console.WriteLine("【1】设备扫描 + 映射");
-            var scanner = new DeviceScanner { Log = m => Info(m) };
-            var houses = scanner.ScanAll();
-            if (houses.Count == 0) { No("未发现任何舱室,硬件未连接?终止"); Done(); return; }
-            Ok($"发现 {houses.Count} 个舱室: {string.Join(",", houses.Select(h => h.HouseSn))}");
-            foreach (var h in houses)
-                Info($"舱{h.HouseSn} @ {h.PortName} 相机#{h.CcdIndex} CCDSN={h.CcdSn} 有相机={h.HasCamera}");
-
-            // ── 2) P0-1 业务闭环:CalibrationManager 参数来源 ──
-            Console.WriteLine("\n【2】P0-1 标定闭环 (CalibrationManager)");
-            TestCalibManager();
-
-            // ── 3+4) 相机通路 + P0-2/3 丢帧曝光跟随 ──
-            Console.WriteLine($"\n【3】相机通路 + P0-2/3 丢帧曝光跟随 (舱{testHouse})");
-            var th = houses.FirstOrDefault(x => x.HouseSn == testHouse && x.HasCamera)
-                  ?? houses.FirstOrDefault(x => x.HasCamera);
-            if (th == null) { No("无可用相机舱,跳过相机测试"); Done(); return; }
-            if (th.HouseSn != testHouse) Info($"舱{testHouse}不可用,改用舱{th.HouseSn}");
-            TestCameraExposure(th);
-
-            Done();
-        }
-
-        static void Done()
-        {
-            Console.WriteLine($"\n==== 结果: {pass} 通过 / {fail} 失败 ====");
-            Console.WriteLine(fail == 0 ? "SMOKE TEST PASS" : "SMOKE TEST HAS FAILURES");
-        }
-
-        // P0-1: 验证 CalibrationManager 优先用JSON、降级EEPROM
-        static void TestCalibManager()
-        {
-            if (!System.IO.File.Exists(JsonPath)) { No($"calibration.json 不存在: {JsonPath}"); return; }
-            var file = CalibrationFile.Load(JsonPath);
-            Info($"JSON: tlSn={file.TlSn} date={file.Date} 舱数={file.Houses.Count}");
-
-            var mgr = new CalibrationManager();
-            // 对JSON里第一个舱的well1: 应判定为已标定并返回JSON的水平脉冲(不读EEPROM,故传null motor安全?)
-            // GetWellParams 合格时不触发EEPROM读取,可传null验证纯JSON路径
-            var hc = file.Houses.FirstOrDefault();
-            if (hc == null) { No("JSON无舱室数据"); return; }
-            var w1 = hc.Wells.FirstOrDefault(w => w.CircleFound && w.PeakRatio > 1.2);
-            if (w1 == null) { Info("JSON中无合格well(都PeakRatio<=1.2),闭环逻辑会全部降级EEPROM——符合空皿预期"); }
-
-            // 验证 HasValidCalibration 判据
-            if (w1 != null)
-            {
-                bool valid = mgr.HasValidCalibration(hc.House, w1.Well);
-                if (valid) Ok($"舱{hc.House} well{w1.Well} 判定为已标定(峰比{w1.PeakRatio:F2}) → 转well将用JSON参数 H={w1.HorizontalPulse} Z={w1.FocusZ} 曝光={w1.Exposure}");
-                else No($"舱{hc.House} well{w1.Well} 应合格却判不合格");
-
-                // 合格路径不读EEPROM,传null motor验证不抛异常
-                try {
-                    var (hp, z, exp) = mgr.GetWellParams(hc.House, w1.Well, null);
-                    if (hp == w1.HorizontalPulse && z == w1.FocusZ && exp == w1.Exposure)
-                        Ok($"GetWellParams 返回JSON参数(未触碰EEPROM): H={hp} Z={z} 曝光={exp}");
-                    else No($"GetWellParams 返回值与JSON不符: H={hp}/{w1.HorizontalPulse} Z={z}/{w1.FocusZ} exp={exp}/{w1.Exposure}");
-                } catch (Exception ex) { No($"合格well不应读EEPROM却抛异常: {ex.Message}"); }
-            }
-
-            // 未标定舱(如99): 应降级,exp返回-1
-            bool invalid = mgr.HasValidCalibration(99, 1);
-            if (!invalid) Ok("未标定舱99 判定为不合格 → 将降级EEPROM (符合预期)");
-            else No("未标定舱99 不应判合格");
-        }
-
-        // P0-2/3: 设两档曝光,丢帧后第2帧亮度应跟随曝光
-        static void TestCameraExposure(HouseDevice h)
-        {
-            HouseMotor motor = null; SerialCamera cam = null;
-            try
-            {
-                motor = new HouseMotor(h.PortName) { Log = _ => { }, MotorDelayMs = 1500 };
-                if (!motor.Open()) { No($"{h.PortName} 打开失败"); return; }
-                cam = new SerialCamera(h.CcdIndex, W, H, 120);
-                if (cam.Init() != 0) { No("相机初始化失败"); return; }
-                cam.SetOpMode(0);
-                motor.OpenLED();
-                Thread.Sleep(300);
-
-                // 抓帧通路
-                if (cam.GrabRgb() != 0) { No("抓帧失败"); return; }
-                var b0 = cam.GetSourceBuffer();
-                if (b0 == null || b0.Length != W * H * 3) { No($"帧尺寸异常: {b0?.Length}"); return; }
-                Ok($"抓帧成功,帧大小 {b0.Length} 字节 ({W}x{H}x3)");
-
-                // P0-2/3 核心: 模拟引擎"设曝光→丢帧→第2帧"序列,验证亮度跟随
-                double lowMean = SetExpGrabStable(cam, 30);
-                double highMean = SetExpGrabStable(cam, 150);
-                Info($"曝光30 → 亮度均值={lowMean:F1}");
-                Info($"曝光150 → 亮度均值={highMean:F1}");
-                if (highMean > lowMean + 3)
-                    Ok($"丢帧后第2帧亮度随曝光上升({lowMean:F1}→{highMean:F1}),无旧帧污染");
-                else
-                    No($"曝光升高但亮度未跟随({lowMean:F1}→{highMean:F1}),可能旧帧污染或光路异常");
-
-                motor.CloseLED();
-            }
-            catch (Exception ex) { No($"相机测试异常: {ex.Message}"); }
-            finally
-            {
-                try { cam?.UnInit(); cam?.Dispose(); } catch { }
-                try { motor?.Close(); motor?.Dispose(); } catch { }
-            }
-        }
-
-        // 复刻 CalibrationEngine 曝光二分里的稳定抓帧: 设曝光→等待→丢第1帧→用第2帧
-        static double SetExpGrabStable(SerialCamera cam, int e)
-        {
-            cam.SetExposure(e);
-            Thread.Sleep(Math.Max(200, e / 5));
-            cam.GrabRgb();                       // 丢弃第1帧(可能旧曝光)
-            cam.GrabRgb();                       // 第2帧
-            return Mean(cam.GetSourceBuffer());
-        }
-
-        static double Mean(byte[] bgr)
-        {
-            long sum = 0; int n = 0;
-            for (int i = 0; i + 2 < bgr.Length; i += 300) { sum += bgr[i] + bgr[i + 1] + bgr[i + 2]; n += 3; }
-            return n > 0 ? sum / (double)n : 0;
-        }
-    }
-}

+ 0 - 43
SmokeTest/SmokeTest.csproj

@@ -1,43 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0-windows</TargetFramework>
-    <Nullable>disable</Nullable>
-    <ImplicitUsings>disable</ImplicitUsings>
-    <Platforms>x64</Platforms>
-    <PlatformTarget>x64</PlatformTarget>
-    <AssemblyName>SmokeTest</AssemblyName>
-    <RootNamespace>AutoFocusTool</RootNamespace>
-    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="System.IO.Ports" Version="8.0.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\Camera\MVCAPI.cs" />
-    <Compile Include="..\Camera\CapInfoStruct.cs" />
-    <Compile Include="..\Camera\Camera.cs" />
-    <Compile Include="..\Serial\Protocol.cs" />
-    <Compile Include="..\Serial\SerialMotor.cs" />
-    <Compile Include="..\Serial\HouseMotor.cs" />
-    <Compile Include="..\Devices\HouseDevice.cs" />
-    <Compile Include="..\Devices\DeviceScanner.cs" />
-    <Compile Include="..\Imaging\Sharpness.cs" />
-    <Compile Include="..\Imaging\WellDetector.cs" />
-    <Compile Include="..\Imaging\ExposureMeter.cs" />
-    <Compile Include="..\Calib\CalibrationFile.cs" />
-    <Compile Include="..\Calib\CalibrationManager.cs" />
-    <Compile Include="SmokeTest.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\DependFile\ccd\*.dll">
-      <Link>DependFile\ccd\%(Filename)%(Extension)</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-
-</Project>