|
|
@@ -1810,7 +1810,6 @@ namespace ivf_tl_Com
|
|
|
custom.commandSource = commandSource;
|
|
|
this.RunState = "自动对焦";
|
|
|
WorkingType = WorkingType.AutoFocusWorking;
|
|
|
- var pictureId = 1;
|
|
|
try
|
|
|
{
|
|
|
int AutofocusEmbryoCount = 0;
|
|
|
@@ -1872,79 +1871,12 @@ namespace ivf_tl_Com
|
|
|
HouseState(commandSource, custom, 0);
|
|
|
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
- // M2-03 改造点2:N 层拍摄位置改用 M2-02 PhotoLayerCalculator(§2.4 公式 + §2.5 就近优先),
|
|
|
- // 取代旧公式(verticalMotorPosition + spacePulse*i,无下移、值缺时硬编码128)。
|
|
|
- // focusZ = currentAutoFocus.verticalMotorPosition (本地对焦算出的最清晰层锚点)
|
|
|
- // 配置就近优先:well(currentHorSetting) 覆盖 → 设备级(TLSetting) → 缺层间距抛 FocusConfigMissingException
|
|
|
- // 层间距未配置 → 写告警日志 + 本 well 本轮跳过(§2.5 不兜底)。
|
|
|
- // ⚠ 待验证 V-046/V-048:层公式真机下发一致(关联 V-002/V-024)、配置缺失对焦失败不崩。
|
|
|
+ // D2-02-T3:对焦图死链已清(评分已删/预览已砍,FocusZ 由 CalibrationEngine 直算,不再拍传对焦图)。
|
|
|
+ // 原"绕 FocusZ 逐层拍对焦图(PhotoLayerCalculator 算层 + VerticalMotorAbsoluteWait 移电机 + Autofocus 抓图存盘上传)"
|
|
|
+ // 整段移除——拍出来无人评、无人看,纯空转。FocusZ 产生/消费(StartAutoFocus/CalibrationEngine/_autoFocusPhoto)不受影响。
|
|
|
+ // 保留:well 遍历骨架、电机就位、LastAutoFocusTimeDic 置位(改无条件,喂服务器查胚胎照位置的时间戳)、isSuccess 返回真(门控 MainThread GetClearest→StartCCD)。
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
- int[] layerPositions;
|
|
|
- try
|
|
|
- {
|
|
|
- var rawCfg = new FocusLayerRawConfig
|
|
|
- {
|
|
|
- WellSpacingPulse = currentHorSetting.focusLayerSpacingPulse,
|
|
|
- WellLayerCount = currentHorSetting.focusLayerCount,
|
|
|
- WellMoveDownLayer = currentHorSetting.moveDownLayer, // well 级下移复用 move_down_layer
|
|
|
- DeviceSpacingPulse = TLSetting.focusLayerSpacingPulse,
|
|
|
- DeviceLayerCount = TLSetting.focusLayerCount,
|
|
|
- DeviceLayerDown = TLSetting.focusLayerDown,
|
|
|
- TlSn = Dish?.tlSn,
|
|
|
- HouseSn = House.houseSn,
|
|
|
- WellSn = itemEmbryo.wellSn
|
|
|
- };
|
|
|
- var layerCfg = PhotoLayerCalculator.Resolve(rawCfg);
|
|
|
- layerPositions = PhotoLayerCalculator.ComputeLayerPositions(
|
|
|
- currentAutoFocus.verticalMotorPosition, layerCfg, TLSetting.verticalMotorPulseMax);
|
|
|
- }
|
|
|
- catch (FocusConfigMissingException exCfg)
|
|
|
- {
|
|
|
- // 配置缺失:本 well 本轮跳过,不用魔法数兜底、不崩采集线程(§2.5)。
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{House.houseSn}][{PortName}][{itemEmbryo.wellSn}号well对焦层配置缺失,跳过该well:{exCfg.Message}]", LogEnum.HouseInfo);
|
|
|
- isSuccess = false;
|
|
|
- continue;
|
|
|
- }
|
|
|
- int currentVer = 0;
|
|
|
- int nextVer = 0;
|
|
|
- bool isEnd = false;
|
|
|
- for (int i = 0; i < layerPositions.Length; i++)
|
|
|
- {
|
|
|
- isEnd = false;
|
|
|
- currentVer = 0;
|
|
|
- nextVer = 0;
|
|
|
- LastRunTime = DateTime.Now;
|
|
|
- this.RunState = $"自动对焦({itemEmbryo.wellSn}-{i + 1})";
|
|
|
- if (IsStopClearest || FirstClearest)
|
|
|
- {
|
|
|
- isSuccess = false;
|
|
|
- break;
|
|
|
- }
|
|
|
- // M2-03:第 i 层绝对 Z 由 PhotoLayerCalculator 算出(含下移偏移 + verticalMotorPulseMax 钳位)。
|
|
|
- currentVer = layerPositions[i];
|
|
|
- nextVer = (i + 1 < layerPositions.Length) ? layerPositions[i + 1] : currentVer;
|
|
|
- if (currentVer > TLSetting.verticalMotorPulseMax) break;
|
|
|
- if (i == (layerPositions.Length - 1) || nextVer > TLSetting.verticalMotorPulseMax) isEnd = true;
|
|
|
- ComBin.VerticalMotorAbsoluteWait(custom, TLSetting.motorDelay, currentVer, currentHorSetting.horizontalMotorPosition, pictureId++, itemEmbryo.wellSn, (i + 1));
|
|
|
- HouseState(commandSource, custom, 0);
|
|
|
- currentHorizontalMotor = ComBin.ReadHorizontalMotorWait(custom);
|
|
|
- currentVerticalMotor = ComBin.ReadVerticalMotorWait(custom);
|
|
|
- var AutofocusResult = Autofocus(custom, isEnd, itemEmbryo, SavePictrueTime, layerPositions.Length, (i + 1));
|
|
|
- if (!AutofocusResult) isSuccess = false;
|
|
|
- if (isEnd && AutofocusResult) LastAutoFocusTimeDic[itemEmbryo.wellSn] = SavePictrueTime;
|
|
|
- HouseState(commandSource, custom, 0);
|
|
|
-
|
|
|
- if (CCDState == 1)
|
|
|
- {
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{itemEmbryo.wellSn}号well第{i + 1}层拍照异常,结束当前well自动对焦]", LogEnum.HouseInfo);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (CCDState == 1)
|
|
|
- {
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{itemEmbryo.wellSn}号well拍照异常,终止全胚胎自动对焦流程]", LogEnum.HouseInfo);
|
|
|
- break;
|
|
|
- }
|
|
|
+ LastAutoFocusTimeDic[itemEmbryo.wellSn] = SavePictrueTime;
|
|
|
}
|
|
|
ComBin.CloseLEDWait(custom);
|
|
|
if (isWuTu)
|
|
|
@@ -1985,10 +1917,6 @@ namespace ivf_tl_Com
|
|
|
return true;
|
|
|
}
|
|
|
bool isSuccess = true;
|
|
|
- int currentVer = 0;
|
|
|
- int nextVer = 0;
|
|
|
- bool isEnd = false;
|
|
|
- int pictureId = 1;
|
|
|
|
|
|
Stopwatch AutofocusThreadStopwatch = Stopwatch.StartNew();
|
|
|
try
|
|
|
@@ -2059,87 +1987,13 @@ namespace ivf_tl_Com
|
|
|
else
|
|
|
{
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
- // V-050:本路径(单/部分胚胎手动对焦)N 层位置统一改用 M2-02 PhotoLayerCalculator
|
|
|
- // (§2.4 公式 + §2.5 就近优先),与主路径 AllEmbryoAutofocus 一致,
|
|
|
- // 取代旧公式(verticalMotorPosition + spacePulse*j / 硬编码128,无下移)。
|
|
|
- // focusZ 锚点 = currentAutoFocus.verticalMotorPosition(本 well 对焦算出的清晰层)。
|
|
|
- // 就近优先:well(currentHorSetting) 覆盖 → 设备级(TLSetting) → 缺层间距抛
|
|
|
- // FocusConfigMissingException,本 well 跳过(continue 外层 well 循环),不兜底、不崩。
|
|
|
- // ⚠ 待验证 V-050:与主路径层公式真机一致、配置缺失不崩。
|
|
|
+ // D2-02-T3:对焦图死链已清(评分已删/预览已砍,FocusZ 由 CalibrationEngine 直算,不再拍传对焦图)。
|
|
|
+ // 原"绕 FocusZ 逐层拍对焦图(PhotoLayerCalculator 算层 + VerticalMotorAbsoluteWait 移电机 + Autofocus 抓图存盘上传)"
|
|
|
+ // 整段移除——纯空转。FocusZ 产生/消费(StartAutoFocus/CalibrationEngine/_autoFocusPhoto)不受影响。
|
|
|
+ // 保留:DeleteAutoFocusWell(对焦队列推进,删了 do-while 死循环)、LastAutoFocusTimeDic 置位(无条件)、isSuccess 返回真。
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
- int[] layerPositions;
|
|
|
- try
|
|
|
- {
|
|
|
- var rawCfg = new FocusLayerRawConfig
|
|
|
- {
|
|
|
- WellSpacingPulse = currentHorSetting.focusLayerSpacingPulse,
|
|
|
- WellLayerCount = currentHorSetting.focusLayerCount,
|
|
|
- WellMoveDownLayer = currentHorSetting.moveDownLayer, // well 级下移复用 move_down_layer
|
|
|
- DeviceSpacingPulse = TLSetting.focusLayerSpacingPulse,
|
|
|
- DeviceLayerCount = TLSetting.focusLayerCount,
|
|
|
- DeviceLayerDown = TLSetting.focusLayerDown,
|
|
|
- TlSn = Dish?.tlSn,
|
|
|
- HouseSn = House.houseSn,
|
|
|
- WellSn = embryoItem.wellSn
|
|
|
- };
|
|
|
- var layerCfg = PhotoLayerCalculator.Resolve(rawCfg);
|
|
|
- layerPositions = PhotoLayerCalculator.ComputeLayerPositions(
|
|
|
- currentAutoFocus.verticalMotorPosition, layerCfg, TLSetting.verticalMotorPulseMax);
|
|
|
- }
|
|
|
- catch (FocusConfigMissingException exCfg)
|
|
|
- {
|
|
|
- // 配置缺失:本 well 本轮跳过,不用魔法数兜底、不崩采集线程(§2.5)。
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{House.houseSn}][{PortName}][{embryoItem.wellSn}号well对焦层配置缺失,跳过该well:{exCfg.Message}]", LogEnum.HouseInfo);
|
|
|
- isSuccess = false;
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (int j = 0; j < layerPositions.Length; j++)
|
|
|
- {
|
|
|
- isEnd = false;
|
|
|
- currentVer = 0;
|
|
|
- nextVer = 0;
|
|
|
- LastRunTime = DateTime.Now;
|
|
|
- if (IsStopClearest || FirstClearest)
|
|
|
- {
|
|
|
- isSuccess = false;
|
|
|
- break;
|
|
|
- }
|
|
|
- this.RunState = $"自动对焦({embryoItem.wellSn}-{j + 1})";
|
|
|
- // V-050:第 j 层绝对 Z 由 PhotoLayerCalculator 算出(含下移偏移 + verticalMotorPulseMax 钳位)。
|
|
|
- currentVer = layerPositions[j];
|
|
|
- nextVer = (j + 1 < layerPositions.Length) ? layerPositions[j + 1] : currentVer;
|
|
|
- if (currentVer > TLSetting.verticalMotorPulseMax)
|
|
|
- {
|
|
|
- //DeleteAutoFocusWell(embryoItem.wellSn);
|
|
|
- //LastAutoFocusTimeDic[embryoItem.wellSn] = SavePictrueTime;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (j == (layerPositions.Length - 1) || nextVer > TLSetting.verticalMotorPulseMax) isEnd = true;
|
|
|
- ComBin.VerticalMotorAbsoluteWait(custom, TLSetting.motorDelay, currentVer, currentHorSetting.horizontalMotorPosition, pictureId++, embryoItem.wellSn, (j + 1));
|
|
|
- HouseState(commandSource, custom, 0);
|
|
|
- currentHorizontalMotor = ComBin.ReadHorizontalMotorWait(custom);
|
|
|
- currentVerticalMotor = ComBin.ReadVerticalMotorWait(custom);
|
|
|
- //抓图
|
|
|
- var AutofocusResult = Autofocus(custom, isEnd, embryoItem, SavePictrueTime, layerPositions.Length, (j + 1));
|
|
|
- HouseState(commandSource, custom, 0);
|
|
|
- if (CCDState == 1)
|
|
|
- {
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{embryoItem.wellSn}号well第{j + 1}层拍照异常,结束当前well自动对焦]", LogEnum.HouseInfo);
|
|
|
- break;
|
|
|
- }
|
|
|
- if (isEnd)
|
|
|
- {
|
|
|
- DeleteAutoFocusWell(embryoItem.wellSn);
|
|
|
- if (AutofocusResult) LastAutoFocusTimeDic[embryoItem.wellSn] = SavePictrueTime;
|
|
|
- }
|
|
|
- }
|
|
|
- if (CCDState == 1)
|
|
|
- {
|
|
|
- isSuccess = false;
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[{embryoItem.wellSn}号well拍照异常,终止部分胚胎自动对焦流程]", LogEnum.HouseInfo);
|
|
|
- break;
|
|
|
- }
|
|
|
+ DeleteAutoFocusWell(embryoItem.wellSn);
|
|
|
+ LastAutoFocusTimeDic[embryoItem.wellSn] = SavePictrueTime;
|
|
|
}
|
|
|
} while (AutoFocusWellAny());
|
|
|
|
|
|
@@ -2666,80 +2520,9 @@ namespace ivf_tl_Com
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 自动对焦抓图
|
|
|
- /// </summary>
|
|
|
- /// <param name="customProtocol">指令数据</param>
|
|
|
- /// <param name="isEnd">是否是最后一张</param>
|
|
|
- /// <param name="leftOffset">左边裁剪像素</param>
|
|
|
- /// <param name="bttOffset">下边裁剪像素</param>
|
|
|
- /// <param name="itemEmbryo">当前抓拍胚胎</param>
|
|
|
- /// <param name="saveDateTime">本轮自动对焦开始时间</param>
|
|
|
- public bool Autofocus(CustomProtocol customProtocol, bool isEnd, Embryo itemEmbryo, DateTime saveDateTime, int totlayer, int currlayer)
|
|
|
- {
|
|
|
- Stopwatch AutofocusOperateStopwatch = Stopwatch.StartNew();
|
|
|
- string content = null;
|
|
|
- bool isSuccess = true;
|
|
|
- try
|
|
|
- {
|
|
|
- int currentver = customProtocol.VerticalMotorPulse;
|
|
|
- content = $"[{House.houseSn}][{PortName}][{customProtocol.commandNumber}][{customProtocol.commandType.ToString()}][图片编号:{customProtocol.pictureId}][图片位置:{customProtocol.well}-{customProtocol.focal}][水平电机:{customProtocol.HorizontalMotorPulse}][垂直电机:{customProtocol.VerticalMotorPulse}][下位机水平:{currentHorizontalMotor}][下位机垂直:{currentVerticalMotor}]";
|
|
|
- if (customProtocol.HorizontalMotorPulse != currentHorizontalMotor || customProtocol.VerticalMotorPulse != currentVerticalMotor)
|
|
|
- {
|
|
|
- TLLogEvent?.Invoke($"{content}垂直电机或水平电机位置和下位机位置不一致", LogEnum.RunError);
|
|
|
- }
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"{content}[自动对焦图片处理开始]", LogEnum.HouseRunRecord);
|
|
|
- if (itemEmbryo.state != 0)
|
|
|
- {
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"{content}[当前胚胎状态:{itemEmbryo.state},跳过抓拍]", LogEnum.HouseRunRecord);
|
|
|
- return false;
|
|
|
- }
|
|
|
- var startElapsed = AutofocusOperateStopwatch.Elapsed;
|
|
|
- int GetRgbData = GetRgbDataFunNew(content, itemEmbryo.wellSn);
|
|
|
- var endElapsed = AutofocusOperateStopwatch.Elapsed;
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[自动对焦抓图耗时:{StringHelper.TimeToString(endElapsed - startElapsed)}][抓图结果:{GetRgbData}]", LogEnum.HouseRunRecord);
|
|
|
- if (GetRgbData != 0)
|
|
|
- {
|
|
|
- TLLogEvent?.Invoke($"{content}自动对焦抓图失败", LogEnum.RunError);
|
|
|
- return false;
|
|
|
- }
|
|
|
- startElapsed = AutofocusOperateStopwatch.Elapsed;
|
|
|
- var path = PathHelper.GetAutoFocusSaveDirectory();
|
|
|
- PathHelper.MakeDirectory(path);//创建目录
|
|
|
- string fileName = $"{House.houseSn}_{itemEmbryo.wellSn}_{Dish.id}_{itemEmbryo.id}_{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}_{customProtocol.HorizontalMotorPulse}_{currentver}.jpg";
|
|
|
- string fullName = $"{path}{fileName}";
|
|
|
- var imagedto = GetImageDTO(itemEmbryo, fileName, path, saveDateTime.ToString("yyyy-MM-dd HH:mm:ss"), 1, totlayer, currlayer, currentver, customProtocol.HorizontalMotorPulse, 0, isEnd);
|
|
|
- SavePicDbEvent?.Invoke(imagedto);
|
|
|
- int score = SaveImage(Camera.SourceBuffer, House.ccdWidth, House.ccdHeight, fullName);
|
|
|
- endElapsed = AutofocusOperateStopwatch.Elapsed;
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"[图片保存耗时:{StringHelper.TimeToString(endElapsed - startElapsed)}][图片保存结果:{score}][注:0表示失败,-1表示异常]", LogEnum.HouseRunRecord);
|
|
|
- if (score != 1)
|
|
|
- {
|
|
|
- TLLogEvent?.Invoke($"{content}图片保存失败[图片保存结果:{score}][注:0表示失败,-1表示异常]", LogEnum.RunError);
|
|
|
- return false;
|
|
|
- }
|
|
|
- var imageBytes = AivfoHelper.GetImageData1(fullName);
|
|
|
- if (imageBytes == null)
|
|
|
- {
|
|
|
- TLLogEvent?.Invoke($"{content}图片不存在:{fullName}", LogEnum.RunError);
|
|
|
- return false;
|
|
|
- }
|
|
|
- imagedto.ImageData = ByteString.CopyFrom(imageBytes);
|
|
|
- UploadImageEvent?.Invoke(imagedto);
|
|
|
- return true;
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- ExceptionLogEvent?.Invoke(ex, $"[{House.houseSn}][{PortName}][{customProtocol.commandNumber}][{customProtocol.commandType.ToString()}]自动对焦图片处理", null, LogEnum.RunException);
|
|
|
- isSuccess = false;
|
|
|
- return false;
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- HouseLogEvent?.Invoke(House.houseSn, DateTime.Now, $"{content}[图片处理结果:{isSuccess}][自动对焦图片处理耗时:{StringHelper.TimeToString(AutofocusOperateStopwatch.Elapsed)}]", LogEnum.HouseRunRecord);
|
|
|
- AutofocusOperateStopwatch.Stop();
|
|
|
- }
|
|
|
- }
|
|
|
+ // D2-02-T3:对焦图死链已清——原 Autofocus(...) 方法(GetRgbDataFunNew 抓帧 + GetImageDTO(...,1,...) + SavePicDbEvent + SaveImage + UploadImageEvent 对焦图存盘上传)
|
|
|
+ // 已整体删除:评分已删、预览已砍,FocusZ 由 CalibrationEngine 直算,不再拍传对焦图。其唯一调用方(All/SingleEmbryoAutofocus 层循环)已同步移除。
|
|
|
+ // 保留 GetImageDTO(Photograph 胚胎照仍用)与 Photograph()(CCD 胚胎照 photographType=0)不动。
|
|
|
|
|
|
/// <summary>
|
|
|
/// 抠图 0表示失败 -1表示异常
|