using System; using System.Collections.Generic; using System.Threading; using ivf_tl_Entity.ComEntitys; using ivf_tl_Entity.CameraEntitys; using ivf_tl_Entity.GlobalEnums; namespace OperateHwTest { /// /// 用 operate 真实业务代码(ivf_tl_Entity.ComEntitys.ComBin)驱动真机的硬件闭环测试。 /// 不是 demo(autofocustool)——这是 operate 实际运行时用的串口封装(命令队列+同步Wait+Channel)。 /// 电机脉冲一律安全钳位(机器空载)。 /// internal class Program { const int Z_MIN = 0, Z_MAX = 120000, H_MIN = 60000, H_MAX = 210000; const int BUFFER_HOUSE = 11; static readonly Dictionary HOUSE_PORT = new Dictionary { {2,"COM4"},{4,"COM9"},{6,"COM11"},{7,"COM18"},{8,"COM5"},{9,"COM19"},{11,"COM3"} }; static int ClampZ(int z) => Math.Max(Z_MIN, Math.Min(Z_MAX, z)); static int ClampH(int h) => Math.Max(H_MIN, Math.Min(H_MAX, h)); static void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}"); static int Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; string cmd = args.Length > 0 ? args[0].ToLower() : "scan"; L($"===== operate 真实代码硬件测试(ivf_tl_Entity.ComBin) 子命令={cmd} ====="); switch (cmd) { case "scan": return CmdScan(); case "motor": return CmdMotor(args); case "http": return CmdHttp(args); case "camera": return CmdCamera(args); default: L("未知子命令"); return 1; } } // ───────────────────────── http:operate 真实 HttpHelper 业务测试 ───────────────────────── static int CmdHttp(string[] args) { string gw = args.Length > 1 ? args[1] : "http://127.0.0.1:10010"; L($"operate 真实 HttpHelper 业务测试 → gateway {gw}"); var log = new ivf_tl_Services.LogHelper(); var http = new ivf_tl_Services.HttpHelper("admin", "123456", log); // Init 内部会 Login(callWebServiceNoToken → HttpClientSendAsync),走 operate 真实 HTTP 收发 var user = http.Init(gw); if (user == null) { L("✗ 登录失败(operate HttpHelper.Init/Login 返回 null)"); return 1; } L($"✓ 登录成功(operate HttpHelper): {Newtonsoft.Json.JsonConvert.SerializeObject(user)}"); // 业务接口:tl-control 已注册 nacos,走真实路由(GetSettingHouseApi 需 tlSn,空库返回空属正常) try { var houses = http.GetSettingHouseApi("house8"); L($"GetSettingHouseApi(tlSn=house8) → 返回 {houses?.Count ?? 0} 条仓室设置(链路通,空属正常)"); } catch (Exception ex) { L($"GetSettingHouseApi 异常: {ex.Message}"); } // surface 接口(修复路由后验证 operate→gateway→surface→DB) try { var quick = http.GetQuickButtonsApi(); L($"GetQuickButtonsApi(surface接口) → 返回 {quick?.Count ?? 0} 个快捷按钮(operate→gateway→surface→DB)"); } catch (Exception ex) { L($"GetQuickButtonsApi 异常: {ex.Message}"); } L("\n===== operate HttpHelper 业务链路测试完成 ====="); return 0; } static ComBin NewCom(int houseSn, string port) { var c = new ComBin(houseSn, port); c.ErrorLogEvent += (msg, lv) => L($" [ERR] {msg}"); c.ExceptionLogEvent += (ex, name, p, lv) => L($" [EX] {name}:{ex?.Message}"); return c; } static int CmdScan() { var online = new List(); foreach (var kv in HOUSE_PORT) { int hs = kv.Key; string port = kv.Value; L($"\n──── 舱{hs} @ {port} ────"); ComBin com = null; try { com = NewCom(hs, port); if (!com.OpenPort()) { L(" ✗ 打开串口失败"); continue; } int sn = com.ShakeHandsWait(new CustomProtocol()); L($" 握手 houseSn={sn}"); if (sn < 0) { L(" ✗ 握手失败"); continue; } online.Add(sn); if (hs == BUFFER_HOUSE) { var (bp, t1, t2) = com.BufferBottleState(new CustomProtocol()); L($" 缓冲瓶: 压力={bp} 温度1={t1}℃ 温度2={t2}℃"); } else { decimal t0 = com.TemperatureWait(new CustomProtocol()); decimal ts = com.ShangTemperatureWait(new CustomProtocol()); decimal tb = com.BoLiTemperatureWait(new CustomProtocol()); decimal pr = com.PressureWait(new CustomProtocol()); State door = com.DoorStatusWait(new CustomProtocol()); int vz = com.ReadVerticalMotorWait(new CustomProtocol()); int hz = com.ReadHorizontalMotorWait(new CustomProtocol()); int ccd = com.GetCCDSNWait(new CustomProtocol()); L($" 温度: 下盖板={t0}℃ 上盖板={ts}℃ 玻璃片下={tb}℃"); L($" 压力={pr} 舱门={door}"); L($" 电机位置: Z={vz} 水平={hz} CCDSN={ccd}"); } } catch (Exception ex) { L($" ✗ 异常: {ex.Message}"); } finally { try { com?.ClosePort(); if (com != null) com.IsStop = true; } catch { } } } L($"\n===== scan 完成,在线舱: {string.Join(",", online)} ====="); return 0; } static int CmdMotor(string[] args) { if (args.Length < 2 || !int.TryParse(args[1], out int hs)) { L("用法: motor "); return 1; } if (!HOUSE_PORT.TryGetValue(hs, out string port)) { L("未知舱"); return 1; } if (hs == BUFFER_HOUSE) { L("缓冲瓶舱无对焦电机"); return 0; } L($"舱{hs} @ {port} 电机安全测试(operate ComBin, 钳位 Z[{Z_MIN},{Z_MAX}] 水平[{H_MIN},{H_MAX}])..."); ComBin com = null; try { com = NewCom(hs, port); if (!com.OpenPort()) { L("✗ 打开串口失败"); return 1; } int sn = com.ShakeHandsWait(new CustomProtocol()); L($"握手 houseSn={sn}"); int z0 = com.ReadVerticalMotorWait(new CustomProtocol()); int h0 = com.ReadHorizontalMotorWait(new CustomProtocol()); L($"原位 Z={z0} 水平={h0}"); int w1h = com.ReadEEPROMhoriMtWellHorHorWait(new CustomProtocol(), 1); L($"well1 EEPROM水平={w1h}"); int ztgt = ClampZ(90000); L($"① Z绝对运动 → {ztgt}..."); bool zok = com.VerticalMotorAbsoluteWait(new CustomProtocol(), 1500, ztgt, h0 < 0 ? 0 : h0, 1, 1, 1); int z1 = com.ReadVerticalMotorWait(new CustomProtocol()); L($" {(zok ? "OK" : "失败")} 读回Z={z1} 偏差={Math.Abs(z1 - ztgt)}"); if (w1h > 0) { int htgt = ClampH(w1h); L($"② 水平绝对运动 → well1 {htgt}..."); bool hok = com.HorizontalMotorAbsoluteWait(new CustomProtocol(), 1500, htgt); int h1 = com.ReadHorizontalMotorWait(new CustomProtocol()); L($" {(hok ? "OK" : "失败")} 读回水平={h1} 偏差={Math.Abs(h1 - htgt)}"); } else L("② well1 EEPROM读取异常,跳过水平运动"); L("③ Z复位 + 水平复位..."); com.VerticalMotorResetWait(new CustomProtocol(), 1500); com.HorizontalMotorResetWait(new CustomProtocol(), 1500); int z2 = com.ReadVerticalMotorWait(new CustomProtocol()); int h2 = com.ReadHorizontalMotorWait(new CustomProtocol()); L($" 复位后 Z={z2} 水平={h2}"); L($"\n===== 舱{hs} 电机(operate ComBin) {(zok ? "✓ 通" : "✗ 失败")} ====="); } catch (Exception ex) { L($"✗ 异常: {ex.Message}"); } finally { try { com?.ClosePort(); if (com != null) com.IsStop = true; } catch { } } return 0; } // ───────────────────────── camera:operate 真实 Camera(MVCAPI) 拍一帧存图 ───────────────────────── // 用法: camera [曝光100us 默认60] [相机台数 默认1]。MV_Usb2Init("MVC2000") 按 filter name 枚举,逐台 Init→SetOpMode→GetRgbData→存图→UnInit。 static int CmdCamera(string[] args) { int exposure = 60; // 安全参数:对焦用~60 (×100us) int count = 1; // 默认只测1台,避免一次拉满7台原生资源 if (args.Length > 1) int.TryParse(args[1], out exposure); if (args.Length > 2) int.TryParse(args[2], out count); if (exposure < 1) exposure = 1; if (exposure > 1000) exposure = 1000; // 曝光钳位[1,1000] int W = 2592, H = 1944; string outDir = System.IO.Path.Combine(AppContext.BaseDirectory, "cam_shots"); System.IO.Directory.CreateDirectory(outDir); L($"相机测试(operate Camera+MVCAPI) 曝光={exposure}×100us 分辨率={W}×{H} 拟测{count}台 → {outDir}"); int ok = 0; for (int i = 0; i < count; i++) { Camera cam = null; try { cam = new Camera(i, W, H, exposure); cam.ExceptionLogEvent += (ex, name, p, lv) => L($" [EX] {name}:{ex?.Message}"); int ri = cam.Init(); // MV_Usb2Init("MVC2000") 内部 out 真实 index L($" 相机#{i} Init={ri} (0=成功)"); if (ri != 0) { L(" ✗ Init 失败,跳过(可能无更多相机/被占用/缺DLL)"); break; } int sn = cam.GetNumbet(); L($" ReadSerial={sn} SN={cam.NumBer}"); int rm = cam.SetOpMode(); L($" SetOpMode={rm}"); cam.SetPartOfCapInfo(exposure); int rg = cam.GetRgbData(); L($" GetRgbData={rg}"); if (rg == 0) { var buf = cam.SourceBuffer; string fn = System.IO.Path.Combine(outDir, $"cam{i}_{cam.NumBer}_{DateTime.Now:HHmmss}.jpg"); bool sv = cam.SavePic(fn, W, H); var fi = sv ? new System.IO.FileInfo(fn) : null; L($" 存图={(sv ? "OK" : "失败")} buf={buf?.Length ?? 0}字节 文件={(fi != null ? fi.Length + "B " + fn : "(无)")}"); if (sv && fi != null && fi.Length > 0) ok++; } else L(" ✗ 抓帧失败"); } catch (Exception ex) { L($" ✗ 相机#{i} 异常: {ex.Message}"); } finally { try { cam?.UnInit(); cam?.DisPose(); } catch { } } } L($"\n===== 相机测试完成,成功出图 {ok} 台 ====="); return 0; } } }