using System; namespace AutoFocusTool.Serial { /// /// 单舱室马达+光源的语义化控制接口。封装 Protocol+SerialMotor, /// 给 UI/对焦逻辑直接调。所有动作同步阻塞到下位机回复。 /// /// 重要:移动是开环+固定延时(motorDelay),下位机回复只代表"收到指令", /// 不代表机械停稳。每次移动后等 MotorDelayMs 再抓图,否则运动模糊。 /// public class HouseMotor : IDisposable { private readonly SerialMotor _serial; /// 电机到位延时(ms),移动后等待机械稳定。真机标定,默认 1500。 public int MotorDelayMs { get; set; } = 1500; public string PortName => _serial.PortName; public bool IsOpen => _serial.IsOpen; public Action Log { get => _serial.Log; set => _serial.Log = value; } public HouseMotor(string portName) { _serial = new SerialMotor(portName); } public bool Open() => _serial.Open(); public void Close() => _serial.Close(); /// 握手,返回下位机自报的 houseSn(失败 -1)。 public int ShakeHands() { var r = _serial.Send(Protocol.ShakeHands()); return r == null ? -1 : Protocol.ParseShakeHands(r); } /// 读本舱绑定的相机序列号 CCDSN(EEPROM,int)。失败 -1。 public int ReadCCDSN() { var r = _serial.Send(Protocol.GetCCDSN()); return r == null ? -1 : Protocol.ParseEepromInt(r); } /// 读 EEPROM 灯光亮度(只读)。失败 -1。 public int ReadLightBrightness() { var r = _serial.Send(Protocol.ReadLightBrightness()); return r == null ? -1 : Protocol.ParseEepromInt(r); } /// 读第 wellNum(1-16) 个well的水平电机位置脉冲(EEPROM)。失败 -1。 public int ReadWellHorizontalPos(int wellNum) { var r = _serial.Send(Protocol.ReadWellHorizontalPos(wellNum)); return r == null ? -1 : Protocol.ParseEepromInt(r); } /// /// 转动培养皿到第 wellNum(1-16) 个well对准相机:读EEPROM该well位置 → 水平电机绝对运动过去。 /// 返回该well的水平脉冲位置,失败 -1。 /// public int RotateToWell(int wellNum) { int pos = ReadWellHorizontalPos(wellNum); if (pos < 0) { Log?.Invoke($"读well{wellNum}位置失败"); return -1; } bool ok = HorizontalMoveTo(pos); Log?.Invoke($"转到well{wellNum} 位置脉冲={pos} {(ok ? "OK" : "失败")}"); return ok ? pos : -1; } /// 读第 wellNum(1-16) 个well的Z对焦零点脉冲(EEPROM)。失败 -1。 public int ReadWellFocusZero(int wellNum) { var r = _serial.Send(Protocol.ReadWellFocusZero(wellNum)); return r == null ? -1 : Protocol.ParseEepromInt(r); } /// 读垂直电机扫描间隔脉冲(每层Z步距,EEPROM)。失败 -1。 public int ReadScanStep() { var r = _serial.Send(Protocol.ReadScanStep()); return r == null ? -1 : Protocol.ParseEepromInt(r); } // ── 光源 ── public bool OpenLED() => _serial.Send(Protocol.OpenLED()) != null; public bool CloseLED() => _serial.Send(Protocol.CloseLED()) != null; // ── 垂直电机(Z=对焦轴)── /// Z 复位回零,等待 MotorDelayMs。 public bool VerticalReset() => _serial.Send(Protocol.VerticalReset(), MotorDelayMs) != null; /// Z 绝对运动到指定脉冲,等待 MotorDelayMs 稳定。 public bool VerticalMoveTo(int pulse) => _serial.Send(Protocol.VerticalAbsolute(pulse), MotorDelayMs) != null; /// Z 绝对运动,自定义稳定延时(ms)。扫描小步移动用短延时提速。 public bool VerticalMoveTo(int pulse, int delayMs) => _serial.Send(Protocol.VerticalAbsolute(pulse), delayMs) != null; /// Z 相对正转 pulse 步。 public bool VerticalForward(int pulse) => _serial.Send(Protocol.VerticalForward(pulse), MotorDelayMs) != null; /// Z 相对反转 pulse 步。 public bool VerticalBackward(int pulse) => _serial.Send(Protocol.VerticalBackward(pulse), MotorDelayMs) != null; /// 读 Z 当前位置脉冲。失败 -1。 public int ReadVerticalPosition() { var r = _serial.Send(Protocol.ReadVerticalMotor()); return r == null ? -1 : Protocol.ParseMotorPosition(r); } // ── 水平电机(皿孔定位)── public bool HorizontalReset() => _serial.Send(Protocol.HorizontalReset(), MotorDelayMs) != null; public bool HorizontalMoveTo(int pulse) => _serial.Send(Protocol.HorizontalAbsolute(pulse), MotorDelayMs) != null; /// 水平绝对运动,自定义稳定延时(ms)。扫描小步移动用短延时提速。 public bool HorizontalMoveTo(int pulse, int delayMs) => _serial.Send(Protocol.HorizontalAbsolute(pulse), delayMs) != null; public bool HorizontalForward(int pulse) => _serial.Send(Protocol.HorizontalForward(pulse), MotorDelayMs) != null; public bool HorizontalBackward(int pulse) => _serial.Send(Protocol.HorizontalBackward(pulse), MotorDelayMs) != null; public int ReadHorizontalPosition() { var r = _serial.Send(Protocol.ReadHorizontalMotor()); return r == null ? -1 : Protocol.ParseMotorPosition(r); } public void Dispose() => _serial.Dispose(); } }