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;
}
}
}