| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- using System;
- using ivf_tl_Entity.CameraEntitys;
- using ivf_tl_Entity.ComEntitys;
- using IvfTl.Hardware;
- using CommunityToolkit.Mvvm.ComponentModel;
- namespace ivf_tl_Operate.ViewModel
- {
- /// <summary>
- /// 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。
- /// </summary>
- 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<string> 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;
- }
- /// <summary>
- /// M2-05 调试页标定相机适配器:把 operate 调试栈已 Init 的 Camera 适配成 ICamera。
- /// 引擎仅用 Width/Height/SetExposure/GrabRgb/GetFrameBuffer;其余成员从不被调用。
- /// </summary>
- 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;
- }
- /// <summary>M2-05 单 well 标定 UI 状态:待标定/标定中/合格(绿)/伪峰(红)/失败(红)。</summary>
- public enum WellCalibState { Pending, Running, Qualified, FakePeak, Failed }
- /// <summary>
- /// M2-05 一键标定单 well UI 结果项(绑定调试页 16 格实时显示)。
- /// 合格(Qualified)绿、伪峰/失败(FakePeak/Failed)红;含 FocusZ/峰比/偏移/曝光/Note。
- /// 实现 ObservableObject 以便每 well 标定完即时刷新对应格子。
- /// 颜色判定建议在 View 用 DataTrigger 绑定 State(绿=Qualified,红=FakePeak|Failed,中性=Pending|Running)。
- /// </summary>
- 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 = "";
- /// <summary>是否合格(供 View 直接绑定做绿/红,等价 State==Qualified)。</summary>
- public bool IsQualified => State == WellCalibState.Qualified;
- }
- /// <summary>
- /// M2-07 对焦后手调拍摄层 · 单层预览项(绑定预览列表,让工程师看到调整后各层绝对 Z)。
- /// 由 PhotoLayerCalculator.ComputeLayerPositions(标定FocusZ, 手调cfg, pulseMax) 生成;
- /// IsFocusLayer 标记清晰层(第 down 层)以便 View 高亮。
- /// </summary>
- public partial class LayerPreviewItem : ObservableObject
- {
- /// <summary>层序号(0 .. count-1,0=对焦起点)。</summary>
- [ObservableProperty] private int layerIndex;
- /// <summary>该层绝对 Z 脉冲(公式算出,含 pulseMax 钳位)。</summary>
- [ObservableProperty] private int zPulse;
- /// <summary>是否为清晰层(即第 down 层,对焦锚点 FocusZ 所在层)。</summary>
- [ObservableProperty] private bool isFocusLayer;
- }
- }
|