using System;
using ivf_tl_Entity.CameraEntitys;
using ivf_tl_Entity.ComEntitys;
using IvfTl.Hardware;
using CommunityToolkit.Mvvm.ComponentModel;
namespace ivf_tl_Operate.ViewModel
{
///
/// M2-05 调试页标定适配器:把 operate 调试栈已打开的 ComBin/Camera 适配成
/// HAL 接口 ISerialChannel/ICamera,供 IvfTl.AutoFocus.CalibrationEngine 复用(不重复实现算法)。
///
/// 【为何用适配器而非 Acquire(OperateDebug) 取 lease.Serial/lease.Camera】
/// 调试页(M1-03)刻意仍用 operate 侧具体类型 ComBin/Camera(方法更全:WriteEEPROM/正反向/RawToRgb),
/// 并已通过 comBin.OpenPort()/camera.Init() 独占持有了本舱 COM 口与相机句柄。
/// 若再向 HAL 申请前台借用并用 lease.Serial(HAL SerialChannelImpl)/lease.Camera(HAL CameraImpl),
/// 会在同一已被调试栈占用的 COM 口/相机上二次 Open/Init → 句柄冲突。
/// 故标定引擎直接驱动调试栈【已打开】的 ComBin/Camera:本适配器只暴露 CalibrationEngine 实际调用的少量方法,
/// 其余接口成员抛 NotSupportedException(引擎从不调用)。互斥仍由调试页持有的 HAL 前台借用(OperateDebug)
/// 保证(打开调试页时 ComHouseInit 已 Acquire(OperateDebug),HAL 暂停 control 对本舱采集)。
///
/// CalibrationEngine 实际用到的依赖面(codegraph 核对 CalibrationEngine.cs):
/// ISerialChannel: HorizontalMoveToWait / VerticalMoveToWait(引擎内);
/// ReadWellHorizontalPosWait / ReadWellFocusZeroWait(VM 取扫描中心,引擎外)。
/// ICamera: Width / Height / SetExposure / GrabRgb / GetFrameBuffer。
///
internal sealed class DebugSerialAdapter : ISerialChannel
{
private readonly ComBin _comBin;
private readonly int _motorDelay;
public DebugSerialAdapter(ComBin comBin, int motorDelay)
{
_comBin = comBin ?? throw new ArgumentNullException(nameof(comBin));
_motorDelay = motorDelay;
}
public Action Log { get; set; }
// ── 引擎实际调用的方法 ──
public bool HorizontalMoveToWait(int pulse, int delayMs = -1)
{
// delayMs<0 时引擎用默认 ScanDelayMs;这里统一用调试页配置的 motorDelay 作为电机到位等待。
return _comBin.HorizontalMotorAbsoluteWait(new CustomProtocol(), _motorDelay, pulse);
}
public bool VerticalMoveToWait(int pulse, int delayMs = -1)
{
// 调试栈垂直绝对运动签名含 hor/pictureId/well/focal(仅日志/记录用),标定阶段填占位 1。
return _comBin.VerticalMotorAbsoluteWait(new CustomProtocol(), _motorDelay, pulse, 1, 1, 1, 1);
}
public int ReadWellHorizontalPosWait(int well)
=> _comBin.ReadEEPROMhoriMtWellHorHorWait(new CustomProtocol(), well);
public int ReadWellFocusZeroWait(int well)
// 调试栈无"按 well 读 Z 焦准零点"命令;读垂直电机清晰起点脉冲作扫描参考(§2.5:EEPROM 仅参考,不进解析链/不回写)。
=> _comBin.ReadEEPROMvertMtStartPulseWait(new CustomProtocol());
// ── 以下接口成员引擎从不调用,调试页标定流程不会触达;保持 ISerialChannel 完整实现 ──
public string PortName => _comBin?.ToString() ?? "";
public bool IsOpen => true;
public int MoveReadTimeoutMs { get; set; } = 12000;
public int QueryReadTimeoutMs { get; set; } = 3000;
public int MotorSettleMs { get; set; } = 1500;
public bool Open() => true; // 调试栈已 OpenPort,HAL 适配层不重复开
public void Close() { } // 句柄生命周期归调试页 ComHouseUnit,适配器不关
public void Dispose() { } // 同上:适配器不持有句柄所有权
private static NotSupportedException NotUsed(string m)
=> new NotSupportedException($"DebugSerialAdapter.{m} 不在调试页标定路径内(CalibrationEngine 未调用)。");
public byte[] SendWait(byte[] frame, int extraWaitMs = 0) => throw NotUsed(nameof(SendWait));
public int ShakeHandsWait() => throw NotUsed(nameof(ShakeHandsWait));
public int ReadCcdSnWait() => throw NotUsed(nameof(ReadCcdSnWait));
public int ReadLightBrightnessWait() => throw NotUsed(nameof(ReadLightBrightnessWait));
public int ReadScanStepWait() => throw NotUsed(nameof(ReadScanStepWait));
public bool WriteWellHorizontalPosWait(int well, int pulseValue) => throw NotUsed(nameof(WriteWellHorizontalPosWait));
public bool VerticalForwardWait(int pulse, int delayMs = -1) => throw NotUsed(nameof(VerticalForwardWait));
public bool VerticalBackwardWait(int pulse, int delayMs = -1) => throw NotUsed(nameof(VerticalBackwardWait));
public bool VerticalResetWait(int delayMs = -1) => throw NotUsed(nameof(VerticalResetWait));
public int ReadVerticalPositionWait() => throw NotUsed(nameof(ReadVerticalPositionWait));
public bool HorizontalForwardWait(int pulse, int delayMs = -1) => throw NotUsed(nameof(HorizontalForwardWait));
public bool HorizontalBackwardWait(int pulse, int delayMs = -1) => throw NotUsed(nameof(HorizontalBackwardWait));
public bool HorizontalResetWait(int delayMs = -1) => throw NotUsed(nameof(HorizontalResetWait));
public int ReadHorizontalPositionWait() => throw NotUsed(nameof(ReadHorizontalPositionWait));
public decimal TemperatureWait() => throw NotUsed(nameof(TemperatureWait));
public decimal ShangTemperatureWait() => throw NotUsed(nameof(ShangTemperatureWait));
public decimal BoLiTemperatureWait() => throw NotUsed(nameof(BoLiTemperatureWait));
public decimal PressureWait() => throw NotUsed(nameof(PressureWait));
public (decimal pressure, decimal t1, decimal t2) BufferBottleStateWait() => throw NotUsed(nameof(BufferBottleStateWait));
public DoorState DoorStatusWait() => throw NotUsed(nameof(DoorStatusWait));
public bool OpenLedWait() => throw NotUsed(nameof(OpenLedWait));
public bool CloseLedWait() => throw NotUsed(nameof(CloseLedWait));
public bool OpenIntakeValveWait() => throw NotUsed(nameof(OpenIntakeValveWait));
public bool CloseIntakeValveWait() => throw NotUsed(nameof(CloseIntakeValveWait));
public bool OpenExhaustValveWait() => throw NotUsed(nameof(OpenExhaustValveWait));
public bool CloseExhaustValveWait() => throw NotUsed(nameof(CloseExhaustValveWait));
public bool HouseAerationWait() => throw NotUsed(nameof(HouseAerationWait));
public bool HouseVentWait() => throw NotUsed(nameof(HouseVentWait));
public bool BufferBottleAerationWait() => throw NotUsed(nameof(BufferBottleAerationWait));
public bool AutoAirSwapWait(bool on) => throw NotUsed(nameof(AutoAirSwapWait));
public object RawComBin => _comBin;
}
///
/// M2-05 调试页标定相机适配器:把 operate 调试栈已 Init 的 Camera 适配成 ICamera。
/// 引擎仅用 Width/Height/SetExposure/GrabRgb/GetFrameBuffer;其余成员从不被调用。
///
internal sealed class DebugCameraAdapter : ICamera
{
private readonly Camera _camera;
public DebugCameraAdapter(Camera camera) => _camera = camera ?? throw new ArgumentNullException(nameof(camera));
public int Index => _camera.Index;
public int Width => _camera.Width;
public int Height => _camera.Height;
public int Exposure => _camera.Exposure;
public string SerialNumber => _camera.NumBer;
public bool IsInit => _camera.IsInit;
public bool IsStart => _camera.IsStart;
// ── 引擎实际调用的方法 ──
public int SetExposure(int exposure100us) => _camera.SetPartOfCapInfo(exposure100us);
public int GrabRgb() => _camera.GetRgbData(); // 调试栈抓帧到内部缓冲,返回 0 成功
public byte[] GetFrameBuffer()
{
try { return _camera.SourceBuffer; } // 24bpp BGR W*H*3,与 af GetSourceBuffer 同语义
catch { return null; } // _pDest 已释放等异常 → null,引擎按抓帧失败重试
}
// ── 以下接口成员引擎从不调用(标定路径不触达);调试页生命周期管句柄,适配器不接管 ──
private static NotSupportedException NotUsed(string m)
=> new NotSupportedException($"DebugCameraAdapter.{m} 不在调试页标定路径内(CalibrationEngine 未调用)。");
public int Init(byte redGain = 25, byte greenGain = 14, byte blueGain = 25) => throw NotUsed(nameof(Init));
public int SetOpMode(byte mode = 0, bool strobe = false) => throw NotUsed(nameof(SetOpMode));
public int ReadSerial() => throw NotUsed(nameof(ReadSerial));
public int UnInit() => throw NotUsed(nameof(UnInit));
public int SetGain(byte red, byte green, byte blue) => throw NotUsed(nameof(SetGain));
public byte[] GrabStable(int preDelayMs = 0, bool discardStale = true, int retry = 2)
{
// 引擎走自带 Grab()(双 Grab 丢帧)而非此便捷重载;仍按"丢一帧+重试"实现以备调用。
if (preDelayMs > 0) System.Threading.Thread.Sleep(preDelayMs);
if (discardStale) { GrabRgb(); }
for (int i = 0; i <= retry; i++)
{
if (GrabRgb() == 0)
{
var b = GetFrameBuffer();
if (b != null && b.Length > 0) return b;
}
System.Threading.Thread.Sleep(50);
}
return null;
}
public int StartPreview(IntPtr hostControl, int left, int top, int width, int height) => throw NotUsed(nameof(StartPreview));
public int StopPreview() => throw NotUsed(nameof(StopPreview));
public void Dispose() { } // 调试页 ComHouseUnit 负责 UnInit,适配器不接管
public object RawCamera => _camera;
}
/// M2-05 单 well 标定 UI 状态:待标定/标定中/合格(绿)/伪峰(红)/失败(红)。
public enum WellCalibState { Pending, Running, Qualified, FakePeak, Failed }
///
/// M2-05 一键标定单 well UI 结果项(绑定调试页 16 格实时显示)。
/// 合格(Qualified)绿、伪峰/失败(FakePeak/Failed)红;含 FocusZ/峰比/偏移/曝光/Note。
/// 实现 ObservableObject 以便每 well 标定完即时刷新对应格子。
/// 颜色判定建议在 View 用 DataTrigger 绑定 State(绿=Qualified,红=FakePeak|Failed,中性=Pending|Running)。
///
public partial class WellCalibUiItem : ObservableObject
{
[ObservableProperty] private int well;
[ObservableProperty] private WellCalibState state = WellCalibState.Pending;
[ObservableProperty] private int focusZ;
[ObservableProperty] private double peakRatio;
[ObservableProperty] private double centerOffsetPct;
[ObservableProperty] private bool circleFound;
[ObservableProperty] private int exposure;
[ObservableProperty] private string note = "";
/// 是否合格(供 View 直接绑定做绿/红,等价 State==Qualified)。
public bool IsQualified => State == WellCalibState.Qualified;
}
///
/// M2-07 对焦后手调拍摄层 · 单层预览项(绑定预览列表,让工程师看到调整后各层绝对 Z)。
/// 由 PhotoLayerCalculator.ComputeLayerPositions(标定FocusZ, 手调cfg, pulseMax) 生成;
/// IsFocusLayer 标记清晰层(第 down 层)以便 View 高亮。
///
public partial class LayerPreviewItem : ObservableObject
{
/// 层序号(0 .. count-1,0=对焦起点)。
[ObservableProperty] private int layerIndex;
/// 该层绝对 Z 脉冲(公式算出,含 pulseMax 钳位)。
[ObservableProperty] private int zPulse;
/// 是否为清晰层(即第 down 层,对焦锚点 FocusZ 所在层)。
[ObservableProperty] private bool isFocusLayer;
}
}