#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：SlideCellFocusingBusiness.py
@Description：
@Author：XSH
@Time：2025/11/15 13:02
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import asyncio
import copy
import numpy as np
import cv2
from AppSettings.Settings import *
from PyQt5                                                      import Qt
from abc                                                        import abstractmethod, abstractclassmethod, abstractstaticmethod
from AppInterfaces.utils                                        import *
from AppUtils.utils                                             import *
from AppCore.Levels                                             import *
from AppCore.DBModels                                           import *
from AppInterfaces.Interfaces                                   import *
from AppCore.Requests                                           import *
from AppBackEnd.AppBusinessController                           import AppBusinessController, AppBusinessInterface
from AppUtils.AppDBUtils                                        import AppDBUtils
from AppCore.AppAlgorithms.ImageAlgorithm import ImageAlgorithm
class SlideCellFocusingBusiness(Qt.QObject, AppBusinessController):

    def __init__(self, parent: AppBusinessInterface = None):
        super(SlideCellFocusingBusiness, self).__init__(parent=parent if parent is not None else parent.m_Control)
        self.initialize()
        self.ScanLevel = ScanStatusLevel.Hig_First_Focus_Status
        self.initModules(parent)

    def initScanLevel(self):
        self.ScanLevel = ScanStatusLevel.Hig_First_Focus_Status

    def initModules(self, parent: AppBusinessInterface = None, **args):
        if isinstance(parent, AppBusinessInterface):
            self.Config                             = parent.config                       #
            self.SystemConfig                       = parent.system                       #
            self.HardWare                           = parent.devices                      #
            self.GlobalStatus                       = parent.status                       #
            self.Application                        = parent.application                  #
            self.Algorithm                          = parent.algorithm                    #
            self.EventBus                           = parent.eventBus                     #

    def isValid(self) -> bool:
        return self.hasGlobalStatus and self.hasSystemConfig and self.hasConfig and self.hasDetector and self.hasMotion

    @property
    def isAvailable(self) -> bool:
        return self.isValid() and self.hasAxisX and self.hasAxisY and self.hasMicroscope

    def Done(self, **args) -> int:
        isWrong: int = RECODE_FAILURE
        self.status.setScanningStatus(self.ScanLevel)
        self.devices.setHeartbeat(False)
        try:
            isSafe          : bool  = AppConvert.ObjectConvert(args.get('isSafe'          , True), bool)
            chamber_number  : int   = AppConvert.ObjectConvert(args.get('chamber_number'  , None), int )
            row_number      : int   = AppConvert.ObjectConvert(args.get('row_number'      , None), int )
            keys: list[str] = list(args.keys())
            _args: dict = {}
            for key in keys:
                if key == 'isSafe':
                    pass
                elif key == 'chamber_number':
                    pass
                elif key == 'row_number':
                    pass
                else:
                    _args[key] = args.get(key, None)
            if isinstance(chamber_number, int) and isinstance(row_number, int):
                self.ChangedScanningStatus(chamber_number, row_number, self.ScanLevel, ObjectiveLevel.Objective100)
                self.eventbus.WebSocketPushScanningData()
                isWrong = self.CellFocusing(chamber_number, row_number, isSafe, **_args)
        except Exception as e:
            AppUtils.print_Exception(e)
        if self.status.isExit:
            self.status.setScanningStatus(ScanStatusLevel.FinishScanning_Status)
        elif self.status.isJumpScanning:
            self.status.setScanningStatus(ScanStatusLevel.JumpScanning_Status)
        elif RECODE_SUCCEED == isWrong:
            self.status.setScanningStatus(self.ScanLevel)
        self.eventBus.WebSocketPushScanningData()
        return

    def WaitDone(self, **args) -> int:
        isWrong: int = RECODE_FAILURE
        self.status.setScanningStatus(self.ScanLevel)
        try:
            isSafe          : bool  = AppConvert.ObjectConvert(args.get('isSafe'          , True), bool)
            chamber_number  : int   = AppConvert.ObjectConvert(args.get('chamber_number'  , None), int )
            row_number      : int   = AppConvert.ObjectConvert(args.get('row_number'      , None), int )
            keys: list[str] = list(args.keys())
            _args: dict = {}
            for key in keys:
                if key == 'isSafe':
                    pass
                elif key == 'chamber_number':
                    pass
                elif key == 'row_number':
                    pass
                else:
                    _args[key] = args.get(key, None)
            if isinstance(chamber_number, int) and isinstance(row_number, int) :
                self.ChangedScanningStatus(chamber_number, row_number, self.ScanLevel, ObjectiveLevel.Objective100)
                self.eventbus.WebSocketPushScanningData()
                isWrong = self.CellFocusing(chamber_number, row_number, isSafe, **_args)
        except Exception as e:
            AppUtils.print_Exception(e)
        if self.status.isExit:
            self.status.setScanningStatus(ScanStatusLevel.FinishScanning_Status)
        elif self.status.isJumpScanning:
            self.status.setScanningStatus(ScanStatusLevel.JumpScanning_Status)
        elif RECODE_SUCCEED == isWrong:
            self.status.setScanningStatus(self.ScanLevel)
        self.eventBus.WebSocketPushScanningData()
        return isWrong


    def CellFocusing(self, chamber_number: int, row_number: int, isSafe: bool = False, **args) -> int:
        from AppLog.logHelper import logSCANNING
        isWrong                 : int = RECODE_FAILURE                          #
        objective               : int                                           #
        focusing_status         : int                                           #
        index                   : int                                           #
        CaseID                  : int = 1                                       #
        MinCellFocusingNumber   : int                                           #
        MaxFocusingCount        : int
        FocusingCount           : int
        MaxCellFocusingNumber   : int
        tray_number             : int = TrayLevel.getTrayNumber(row_number)     #
        Preview                 : Previewsettings                               #
        Collect                 : Collectsettings                               #
        PreviewClassifier       : PreviewClassifierModel                        #
        CollectClassifier       : CollectClassifierModel                        #
        Objective               : ObjectiveModel                                #
        SlideTable              : TrayModel                                     #
        Focusing                : FocusingConfigModel                           #
        CellModelArrays         : list[CellModel]                               #
        CaseModels              : list[CellModel] = []                          #
        objective           = ObjectiveLevel.Objective100
        focusing_status     = ScanStatusLevel.Hig_First_Focus_Status
        Objective           = ObjectiveModel.getConfig(objective)
        SlideTable          = TrayModel.getConfig(tray_number)
        if not isinstance(Objective, ObjectiveModel) or not isinstance(SlideTable, TrayModel):
            return RECODE_FAILURE
        SCANNING_STATUS = self.process_scanning_status()
        if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
            self.camera.Close()
            self.eventbus.SaveSpecimen(chamber_number, row_number)
            self.finishCellFocusing(chamber_number, row_number)
            return RECODE_SUCCEED
        isWrong, MinCellFocusingNumber, CellModelArrays = self.readyCellFocusing(chamber_number, row_number, tray_number, objective)
        SCANNING_STATUS = self.process_scanning_status()
        if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
            self.camera.Close()
            self.eventbus.SaveSpecimen(chamber_number, row_number)
            self.finishCellFocusing(chamber_number, row_number)
            return RECODE_SUCCEED
        if RECODE_SUCCEED != isWrong or not isinstance(CellModelArrays, (tuple, list)) or len(CellModelArrays) <= 0:
            self.camera.Close()
            self.eventbus.SaveSpecimen(chamber_number, row_number)
            self.finishCellFocusing(chamber_number, row_number)
            return isWrong
        template, Preview, Collect = self.status.getSlideScanTemplate(chamber_number, row_number)
        if not isinstance(template, ScanTemplate) or not isinstance(Preview, Previewsettings) or not isinstance(Collect, Collectsettings):
            self.camera.Close()
            self.eventbus.SaveSpecimen(chamber_number, row_number)
            self.finishCellFocusing(chamber_number, row_number)
            return isWrong
        """设置光源参数"""
        self.motion.changedLightIntensity(objective)
        FocusingCount           = 0
        index                   = 0
        item                    = CellModelArrays[0]
        classifier              = Collect.classifier if Collect.has_classifier else 'Aberration'
        WebSlot.WebServerReceivedSignal.emit(Request.APIInstance(WebAPILevel.getScanningData))
        """为了防止刚开始定焦时，因重新打开相机和设置参数之后 获取图像错误问题  重新打开相机后等3秒开始定焦"""
        self.waitForStartClassifierFocusing(item.copy(), classifier, 1000)
        while index < len(CellModelArrays):
            self.ChangedScanningStatus(chamber_number, row_number, self.ScanLevel, objective)
            Objective       = ObjectiveModel.getConfig(objective)
            focusing_time   = Qt.QDateTime.currentDateTime()
            Collect         = Collectsettings.Primary(Collect.ID)
            classifier      = Collect.classifier if Collect.has_classifier else 'Aberration'
            """获取当前定焦的最大重试次数 如果超过最大重试次数 直接退出"""
            Focusing = FocusingConfigModel.getConfig(objective, ScanStatusLevel.Hig_Four_Focus_Status)
            MaxFocusingCount = Focusing.FocusingRetryCount if Focusing.hasFocusingRetryCount else 20
            """获取要扫描的核型数量 如果数量 <= 0 则扫描全部 """
            MaxCellFocusingNumber = Collect.cell_count if Collect is not None and Collect.has_cell_count else 0
            if MaxCellFocusingNumber > 0:
                if len(CaseModels) - 1 >= MaxCellFocusingNumber:
                    break
            item = CellModelArrays[index]
            try:
                SCANNING_STATUS = self.process_scanning_status()
                if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
                    self.microscope.stop_z()
                    break
                isWrong, FocusingModel = self.waitForSlideClassifierFocusing(item, Objective, classifier, SlideTable)
                if RECODE_SUCCEED == isWrong and isinstance(FocusingModel, SlideModelInterface):
                    isWrong = self.ProcessCellFocusing(CaseID, classifier, FocusingModel, CaseModels)
                    if RECODE_SUCCEED == isWrong:
                        FocusingCount = 0
                    else:
                        FocusingCount += 1
                else:
                    FocusingCount += 1
                if FocusingCount > MaxFocusingCount:
                    break
                SCANNING_STATUS = self.process_scanning_status()
                if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
                    self.microscope.stop_z()
                    break
            except Exception as focusing_ex:
                AppUtils.print_Exception(focusing_ex)
            index += 1
            logSCANNING << f"定焦时间为:{focusing_time.msecsTo(Qt.QDateTime.currentDateTime())}ms"
        self.focusingevent      .waitForEmpty()
        self.cellfocusingevent  .waitForEmpty()
        """清空显示界面"""
        AppDBUtils.quick_sort(CaseModels, 0, len(CaseModels) - 1)
        Models: list = CaseModels[::-1]
        image: np.ndarray
        case_model: SlideModelInterface
        for CaseID, case_model in enumerate(Models):
            if case_model.hasImageSource:
                """更新数据"""
                case_model.Index = CaseID + 1
                case_model.Level = ScanStatusLevel.HigKaryotypePicture_Status
                image = case_model.toImage()
                SlideCaseModel.model_update(case_model.to_field(is_primary=False), SlideCaseModel.ID == case_model.ID)
                self.eventbus.SaveSlideModel(case_model, image, False, True)
            AppTimeLoop.processGlobalEvents(200)
        self.process_scanning_status()
        self.focusingevent      .waitForEmpty()
        self.cellfocusingevent  .waitForEmpty()
        self.status.setCollectCount(chamber_number, row_number, tray_number, objective, len(CaseModels))
        """保存样本信息"""
        self.eventbus.SaveSpecimen(chamber_number, row_number)
        AppTimeLoop.processGlobalEvents(1000)
        self.camera.Close()


        self.finishCellFocusing(chamber_number, row_number)
        return RECODE_SUCCEED


    def readyCellFocusing(self, chamber_number: int, row_number: int, tray_number: int, objective: int) -> (int, int, list[CellModel]):
        isWrong                 : int                       = RECODE_FAILURE #
        old_objective           : int                                        #
        MinCellFocusingNumber   : int                       = 0              #
        Focusing                : FocusingConfigModel                        #
        SlideTable              : TrayModel                                  #
        Objective               : ObjectiveModel                             #
        CellModelArrays         : list[CellModel]           = []             #
        TrayBeginAction         : SlideFocusingModel                         #
        SlideStatus             : ChamberStatusModel                         #
        """如果当前模板不可用 直接退出"""
        if not self.isAvailable:
            return isWrong, MinCellFocusingNumber, CellModelArrays
        """初始化数据模型"""
        self.initClassifier()
        """如果当前托盘没有玻片"""
        """传感器位置不对，暂时不用此功能"""
        # if not self.manipulator.isTrayAvailable():
        #     return isWrong, MinCellFocusingNumber, CellModelArrays
        self.ChangedScanningStatus(chamber_number, row_number, self.ScanLevel, objective)
        slide: ChamberStatusModel = ChamberStatusModel.getChamberSlide(chamber_number, row_number)
        self.status.setTrayTableSlide(chamber_number, row_number, tray_number, level=self.ScanLevel, CodeString=slide.CodeString, objective=objective)
        Objective       = ObjectiveModel.getConfig(objective)
        if Objective is None :
            return isWrong, MinCellFocusingNumber, CellModelArrays
        SCANNING_STATUS = self.process_scanning_status()
        if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
            return RECODE_SUCCEED, MinCellFocusingNumber, CellModelArrays
        """初始化Z轴范围"""
        self.microscope.init_z_range()
        """设置Z轴定焦范围"""
        self.microscope.set_z_upper_limit(Objective.Focus_Upper_Limit)
        """设置Z轴速度"""
        self.microscope.set_z_speed(Objective.Default_Speed)
        """设置当前物镜"""
        ObjectiveConfig1: ObjectiveModel = ObjectiveModel.getConfig(ObjectiveLevel.Objective1)
        _, old_objective = self.microscope.get_objective()
        if RECODE_SUCCEED != _ or old_objective != objective:
            # 移动Z轴到6mm安全位置
            self.microscope.waitmove_z(ObjectiveConfig1.Safe_Limit)
            _ = self.microscope.set_objective(level=objective, is_lower_limit=False)
        if RECODE_SUCCEED != _:
            AppTimeLoop.processEvents(1000)
            _, old_objective = self.microscope.get_objective()
            if RECODE_SUCCEED != _ or old_objective != objective:
                _ = self.microscope.set_objective(level=objective, is_lower_limit=False)

        if RECODE_SUCCEED != _:
            AppTimeLoop.processEvents(1000)
            _, old_objective = self.microscope.get_objective()
            if RECODE_SUCCEED != _ or old_objective != objective:
                return isWrong, CellModelArrays
        """设置光源参数为10倍物镜光源"""
        self.microscope.changedLightIntensity(ObjectiveLevel.ObjectiveNone, isChanged=True)
        """关闭相机"""
        self.camera.Close()
        """获取低倍物镜识别的所有核型"""
        CellModelArrays = self.getArrays(chamber_number, row_number, tray_number, objective)
        if CellModelArrays is None or len(CellModelArrays) <= 0:
            self.status.setPreviewCount(chamber_number, row_number, tray_number, objective, 0)
            return isWrong, MinCellFocusingNumber, CellModelArrays
        self.status.setPreviewCount(chamber_number, row_number, tray_number, objective,len(CellModelArrays))
        template, Preview, Collect = self.status.getSlideScanTemplate(chamber_number, row_number)
        if not isinstance(template, ScanTemplate) or not isinstance(Preview, Previewsettings) or not isinstance(Collect, Collectsettings):
            return isWrong, MinCellFocusingNumber, CellModelArrays
        MinCellFocusingNumber = Preview.warning_value if Preview.has_warning_value else 0
        is_warning_scan = Preview.is_warning_scan if Preview.has_is_warning_scan else 0
        """如果当前核型数量小于预警数量"""
        if len(CellModelArrays) <= MinCellFocusingNumber:
            """不是调试模式"""
            """小于预警数量不扫描 退出"""
            if not is_warning_scan:
                MinCellFocusingNumber = 0
                self.status.setScanningStatus(ScanStatusLevel.CellEmpty_Status)
                return RECODE_SUCCEED, MinCellFocusingNumber, CellModelArrays
        SCANNING_STATUS = self.process_scanning_status()
        if SCANNING_STATUS == RECODE_CHANGED or SCANNING_STATUS == RECODE_JUMPING:
            return RECODE_SUCCEED, MinCellFocusingNumber, CellModelArrays
        """移动到起始位置"""
        begin: CellModel = CellModelArrays[0]
        position_x: float = begin.Position_X
        position_y: float = begin.Position_Y
        position_z: float = begin.Position_Z - abs(Objective.FocalDistance)

        self.microscope.waitmove_z(position_z)
        self.manipulator.reload_axis_speed()
        self.manipulator.waitmove_xy(position_x, position_y)
        """初始化定焦线程"""
        if not self.hasFocusing:
            self.initEventBus()
            self.eventbus.initFocusing()
        """打开相机 并设置为定焦采集模式"""
        self.camera.ReOpen(objective)
        self.status.setCameraModel(objective)
        self.camera.StartFocusing(objective)
        _, old_objective = self.microscope.get_objective()
        isWrong = RECODE_SUCCEED if self.camera.IsGrabbing and old_objective == objective else RECODE_FAILURE
        return isWrong, MinCellFocusingNumber, CellModelArrays

    def ProcessCellFocusing(self, CaseID: int, classifier: str, focusing_model: CellModel, Cases: list[CellModel]) -> int:
        from AppLog.logHelper import logSCANNING
        isWrong             : int = RECODE_FAILURE                              #
        focusing_status     : int = ScanStatusLevel.Hig_Four_Focus_Status       #
        slide               : SlideCaseModel                                    #
        Objective           : ObjectiveModel                                    #
        Focusing            : FocusingConfigModel                               #
        imageParameter      : ImageAnalysisModel                                #
        CameraConfig        : CameraInterface                                   #
        HigCellModel        : HigObjectiveCellModel                             #
        StripCount          : int                                               #
        ImageScore          : int                                               #
        MinStripCount       : int                                               #
        MaxStripCount       : int                                               #
        StripCount          : int                                               #
        StripCounts         : list[int]                                         #
        IntegrationImages   : list[np.ndarray]                                  #

        if not isinstance(focusing_model, SlideModelInterface) or not focusing_model.has_Collect:
            return isWrong
        slide       = SlideCaseModel.copyInstance(focusing_model)
        slide.Level = focusing_status
        Objective   = slide.Objective if slide.hasObjective else ObjectiveModel.getConfig(slide.ObjectiveLevel)
        Focusing    = FocusingConfigModel.getConfig(Objective.Level, focusing_status)
        slide.Objective = Objective
        slide.Focusing  = Focusing
        try:
            Position_Z = focusing_model.Position_Z
            if int(Position_Z) < int(Objective.Focus_Lower_Limit):
                return isWrong
            imageParameter      = ImageAnalysisModel.getConfig(classifier)
            IntegrationImages   = []
            CameraConfig = self.status.getCameraModel(Objective.Level)
            self.camera.setModel(Objective.Level)
            self.microscope.changedLightIntensity(Objective.Level)
            """清除Z轴范围"""
            self.microscope.wait_init_z_range()
            self.microscope.set_z_speed(Objective.Default_Speed)
            self.microscope.set_z_move_mode(1)
            """移动Z轴到焦点位置"""
            self.microscope.waitmove_z(Position_Z)
            """如果需要融合图像"""
            if imageParameter.hasIntegration and imageParameter.isIntegration:
                """融合图像"""
                ExposureTimes = IntegrationExposureTimes.ClassifierExposureTimes(parent_id=slide.collect.ID)
                if not isinstance(CameraConfig, CameraInterface):
                    return isWrong
                if ExposureTimes is None or len(ExposureTimes) <= 0:
                    if self.isAberration(classifier):
                        ExposureTimes = [CameraConfig.AberrationExposureTime]
                    elif self.isKaryotyping(classifier):
                        ExposureTimes = [CameraConfig.KaryotypingExposureTime]
                    else:
                        ExposureTimes = [CameraConfig.ExposureTime]
                """移动Z轴到焦点位置"""
                self.microscope.waitmove_z(Position_Z)
                """采集多张图像"""
                self.camera.RequestMultipleArray(CameraConfig, ExposureTimes, IntegrationImages)
                if len(IntegrationImages) <= 0:
                    return RECODE_FAILURE
                base_image = IntegrationImages[0]
                # HigCellModel = HigObjectiveCellModel.instance(classifier=classifier, model=slide, image=None, cases=Cases, exposureTimes=ExposureTimes, imageParameter=imageParameter, IntegrationImages=IntegrationImages)

            elif imageParameter.hasCameraHDR and imageParameter.isCameraHDR:
                ExposureTimes = IntegrationExposureTimes.ClassifierExposureTimes(parent_id=slide.collect.ID)
                if ExposureTimes is None or len(ExposureTimes) <= 0:
                    if self.isAberration(classifier):
                        ExposureTimes = [CameraConfig.AberrationExposureTime]
                    elif self.isKaryotyping(classifier):
                        ExposureTimes = [CameraConfig.KaryotypingExposureTime]
                    else:
                        ExposureTimes = [CameraConfig.AberrationExposureTime]
                """移动Z轴到焦点位置"""
                self.microscope.waitmove_z(Position_Z)
                """采集多张图像"""
                self.camera.RequestMultipleArray(CameraConfig, ExposureTimes, IntegrationImages)
                if len(IntegrationImages) <= 0:
                    return RECODE_FAILURE
                base_image = IntegrationImages[0]
                # HigCellModel = HigObjectiveCellModel.instance(classifier=classifier, model=slide, image=None, cases=Cases, exposureTimes=ExposureTimes, imageParameter=imageParameter, IntegrationImages=IntegrationImages)
            else:
                if imageParameter.isFocusingHDR:
                    ExposureTimes = IntegrationExposureTimes.ClassifierHDRTimes(parent_id=slide.collect.ID)
                elif self.isAberration(classifier):
                    ExposureTimes = [CameraConfig.AberrationExposureTime]
                elif self.isKaryotyping(classifier):
                    ExposureTimes = [CameraConfig.KaryotypingExposureTime]
                else:
                    ExposureTimes = [CameraConfig.ExposureTime]
                if ExposureTimes is None or len(ExposureTimes) <= 0:
                    if imageParameter.isFocusingHDR:
                        ExposureTimes = [0]
                    elif self.isAberration(classifier):
                        ExposureTimes = [CameraConfig.AberrationExposureTime]
                    elif self.isKaryotyping(classifier):
                        ExposureTimes = [CameraConfig.KaryotypingExposureTime]
                    else:
                        ExposureTimes = [CameraConfig.AberrationExposureTime]
                """小范围定焦 """
                slide.Level = ScanStatusLevel.Hig_Four_Focus_Status
                isWrong, _, __, focusing_item = self.StartCellClassifierFocusing(slide, Objective, focusing_status, classifier, True)
                if RECODE_SUCCEED != isWrong or not isinstance(focusing_item, SlideModelInterface) or not focusing_item.hasImageSource:
                    return RECODE_FAILURE
                """获取定焦后的图片"""
                base_image = focusing_item.toImage()

                for ExposureTime in ExposureTimes:
                    if imageParameter.isFocusingHDR:
                        if isinstance(ExposureTime, int) and int(ExposureTime) == 0:
                            IntegrationImages.append(base_image.copy())
                        elif isinstance(ExposureTime, float) and Float(ExposureTime) == 0.0:
                            IntegrationImages.append(base_image.copy())
                        else:
                            IntegrationImages.append(ImageAlgorithm.shadow(base_image.copy(), float(ExposureTime)))
                    else:
                        IntegrationImages.append(base_image.copy())
                    AppTimeLoop.processGlobalEvents(200)

                """获取定焦后的图片"""
                base_image      = focusing_item.toImage()
                slide           = SlideCaseModel.copyInstance(focusing_item)
                slide.Level     = ScanStatusLevel.HigKaryotypePicture_Status
                slide           .setImageSource(base_image.copy())
                # HigCellModel = HigObjectiveCellModel.instance(classifier=classifier, model=slide, image=base_image, cases=Cases, exposureTimes=ExposureTimes, imageParameter=imageParameter, IntegrationImages=[])

            if len(IntegrationImages) <= 0:
                return RECODE_FAILURE

            """发送图像到界面 融合图像 保存图像 """
            if not self.hasCellFocusing:
                self.initEventBus()
                self.eventbus.initCellFocusing()
            if not self.hasCellFocusing:
                return RECODE_FAILURE
            Cases.append(slide)
            HigCellModel: HigObjectiveCellModel = HigObjectiveCellModel.instance(classifier=classifier, model=slide, image = base_image.copy(), cases=Cases, exposureTimes=ExposureTimes, IntegrationImages=IntegrationImages, imageParameter=imageParameter)
            self.cellfocusingevent.SendEvent(HigCellModel)
            isWrong = RECODE_SUCCEED
        except Exception as e:
            AppUtils.print_Exception(e)
            isWrong = RECODE_FAILURE
        return isWrong


    def waitForSlideClassifierFocusing(self, model: CellModel, Objective: ObjectiveModel, classifier: str, SlideTable: TrayModel) -> (int, CellModel):
        isWrong                     : int                   = RECODE_FAILURE                                            #
        focusing_index              : int
        focusing_b_position_z       : int
        focusing_e_position_z       : int
        focusing_z_speed            : int
        focusing_z_range            : int
        index                       : int
        Index                       : int                   = 0                                                         # 定焦序号
        objective                   : int
        _Objective                  : ObjectiveModel
        result_focusing_model       : CellModel
        last_focusing_model         : CellModel                                                                         # 上次定焦数据
        next_focusing_model         : CellModel                                                                         # 返回定焦数据
        focusing_model              : CellModel                                                                         # 当前定焦数据
        front_focusing_model        : CellModel                                                                         # 首次定焦数据
        focusing_center_model       : CellModel                                                                         # 核型中心点数据
        focusing_status             : int                   = ScanStatusLevel.Hig_First_Focus_Status                    # 定焦状态
        focusing_count              : int                   = Objective.FocusingCount                                   # 定焦次数
        MinFocusingCount            : int                   = 1                                                         # 最小定焦次数
        MaxFocusingCount            : int                   = 4                                                         # 最大定焦次数
        FocusingItems               : list[CellModel]       = []
        Focusing                    : FocusingConfigModel   = FocusingConfigModel.getConfig(Objective.Level, focusing_status)
        """第一次定焦 如果定焦成功，移动位置到核型中心点"""
        isWrong, Index, front_focusing_model = self.getCellCenterModel(classifier, model, Objective, SlideTable)
        if RECODE_SUCCEED != isWrong or not isinstance(front_focusing_model, SlideModelInterface):
            return RECODE_FAILURE, None
        """移动位置到核型中心"""
        isWrong, focusing_center_model = self.moveToCellCenter(classifier, model.copy(), front_focusing_model, Objective)
        if RECODE_SUCCEED != isWrong or not isinstance(focusing_center_model, SlideModelInterface):
            return RECODE_FAILURE, None
        """开始多次定焦"""
        last_focusing_model                      = None
        next_focusing_model                      = None
        focusing_model                           = None
        objective                                = Objective.Level
        imageParameter: ImageAnalysisModel = ImageAnalysisModel.getConfig(classifier)
        if (imageParameter.hasIntegration and imageParameter.isIntegration) or (imageParameter.hasCameraHDR and imageParameter.isCameraHDR):
            for focusing_index in range(MinFocusingCount, max(MinFocusingCount + 1, focusing_count)):

                """处理停止和暂停"""
                while self.status.waitForPauseRunning():
                    AppTimeLoop.processEvents(self.system.WarningWaitTimes())
                if self.status.waitForExitRunning():
                    break
                if focusing_index == 1:
                    focusing_status = ScanStatusLevel.Hig_Second_Focus_Status
                elif focusing_index == 2:
                    focusing_status = ScanStatusLevel.Hig_Three_Focus_Status
                else:
                    focusing_status = ScanStatusLevel.Hig_Four_Focus_Status
                if isinstance(last_focusing_model, SlideModelInterface):
                    focusing_model              = last_focusing_model.copy  ()
                else:
                    focusing_model              = focusing_center_model.copy()
                Focusing                        = FocusingConfigModel.getConfig(Objective.Level, focusing_status)
                _Objective                      = ObjectiveModel.getConfig(objective)
                focusing_model.Focusing         = Focusing
                focusing_model.Objective        = _Objective
                self.camera.setModel(objective)
                self.motion.changedLightIntensity(objective)
                isWrong, _, __, next_focusing_model =  self.StartCellClassifierFocusing(focusing_model, _Objective, focusing_status, classifier)
                if RECODE_SUCCEED == isWrong and isinstance(next_focusing_model, SlideModelInterface):
                    # self.CompleteFocusingModel(next_focusing_model)
                    last_focusing_model = next_focusing_model
                    FocusingItems.append(next_focusing_model.copy())
        else:
            last_focusing_model = focusing_center_model
        if len(FocusingItems) >= 1:
            result_focusing_model = FocusingItems[-1].copy()
        elif isinstance(last_focusing_model, SlideModelInterface):
            result_focusing_model = last_focusing_model.copy()
        else:
            result_focusing_model = None
        isWrong = RECODE_SUCCEED if isinstance(result_focusing_model, SlideModelInterface) else RECODE_FAILURE
        return isWrong, result_focusing_model

    def getCellCenterModel(self, classifier: str, model: SlideModelInterface, Objective: ObjectiveModel, SlideTable: TrayModel) -> (int, int, SlideModelInterface):
        isWrong : int = RECODE_FAILURE
        index   : int                                   # 数据序号
        slide_count: int = 0
        result_focusing_model: SlideModelInterface      #
        """判断当前玻片是否已经扫描过 如果已经扫描 则按加过油的焦点距离"""
        slide_count = AlreadyScannedModel.filter_count(AlreadyScannedModel.CodeString == model.CodeString)
        if slide_count > 0:
            FocalDistance = abs(int(Objective.OilFocalDistance))
        else:
            FocalDistance = abs(int(Objective.FocalDistance))
        index                           = 1
        focusing_status                 = ScanStatusLevel.Hig_First_Focus_Status
        Focusing                        = FocusingConfigModel.getConfig(Objective.Level, focusing_status)
        Focus_Lower_Limit               = Objective.Lower_Limit
        Focus_Upper_Limit               = Objective.Focus_Upper_Limit
        focusing_length                 = Focusing.FocusingRange
        focusing_z_speed                = Focusing.FocusingSpeed
        objective_focusing_positions    = model.Position_Z              #
        objective_focusing_difference   = abs(model.Position_Z) - FocalDistance
        focusing_b_z                    = max(min(int(objective_focusing_difference - focusing_length), Focus_Upper_Limit), Focus_Lower_Limit)
        focusing_e_z                    = max(min(int(objective_focusing_positions  + focusing_length), Focus_Upper_Limit), Focus_Lower_Limit)
        item                            = model.copy()
        item.Index                      = index
        item.Focusing                   = Focusing
        item.Level                      = focusing_status
        """设置系统状态"""
        self.status.setTrayTableSlide(chamber_number    =item.ChamberGroupNumber,
                                      row_number        =item.ChamberRowNumber  ,
                                      tray_number       =item.TrayNumber        ,
                                      Level             =focusing_status        ,
                                      CodeString        =item.CodeString         )
        self.manipulator.waitmove_xy(item.Position_X, item.Position_Y)
        """设置Z轴速度"""
        self.microscope.set_z_speed(Objective.Default_Speed)
        """清除Z轴限位"""
        self.microscope.init_z_range()
        self.microscope.changedLightIntensity(Objective.Level)
        self.camera.setModel(Objective.Level)
        isWrong, result_focusing_lower_z, result_focusing_upper_z, result_focusing_model, isUpWard = self.StartCustomClassifierFocusing(item, Objective, focusing_b_z, focusing_e_z, focusing_z_speed, focusing_status, classifier)
        if RECODE_SUCCEED == isWrong and isinstance(result_focusing_model, SlideModelInterface):
            result_focusing_model.Focusing = Focusing
        else:
            result_focusing_model = None
        self.microscope.stop_move()

        return isWrong, index, result_focusing_model

    def moveToCellCenter(self, classifier: str, item: CellModel, focusing_model: CellModel, Objective: ObjectiveModel) -> (int, CellModel):
        from AppLog.logHelper import logSCANNING
        if int(focusing_model.Position_Z) <= int(Objective.Safe_Limit):
            return RECODE_FAILURE, None
        if not focusing_model.hasImageSource:
            return RECODE_FAILURE, None
        image: np.ndarray = focusing_model.toImage()
        if image is None:
            return RECODE_FAILURE, None
        if not self.isFocusingCellAvailable(focusing_model):
            return RECODE_FAILURE, None
        tray_model: TrayModel = TrayModel.getConfig(item.TrayNumber)
        MinStripCount: int = focusing_model.Focusing.MinStripCount
        MaxStripCount: int = focusing_model.Focusing.MaxStripCount
        begin: Qt.QDateTime = Qt.QDateTime.currentDateTime()

        score, cellCount, center_point = self.Classifier.process(level=classifier, image=image.copy())
        print(f"识别核型时长: {begin.msecsTo(Qt.QDateTime.currentDateTime())}ms 识别到条带数量: {cellCount} 中心点 {center_point}")
        if cellCount < MinStripCount or cellCount > MaxStripCount:
            if focusing_model.hasPreview and focusing_model.preview.is_save:
                name: str = AppConvert.QDateTimeMsecToPath(Qt.QDateTime.currentDateTime(), '_') + f'_{cellCount}.jpg'
                savePath: str = f'{item.collect.save_address}/Other/{name}'
                info: Qt.QFileInfo = Qt.QFileInfo(savePath)
                self.eventbus.SaveImage(info.absoluteFilePath(), image.copy())
            print(f'检测到核型数量为{cellCount} 不在阀值范围内 {cellCount} < {MinStripCount} or {cellCount} > {MaxStripCount} ')
            return RECODE_FAILURE, None
        try:
            """中心点"""
            centerX = int(center_point[0])
            centerY = int(center_point[1])
        except Exception as e:
            AppUtils.print_Exception(e)
            return RECODE_FAILURE, None
        item.ImageCellX = centerX
        item.ImageCellY = centerY
        CellModel.set100XCellPoint(tray_model, item)
        item.Position_Z = float(Decimal(f'{focusing_model.Position_Z}'))
        self.manipulator.waitmove_xy(item.Position_X, item.Position_Y)
        return RECODE_SUCCEED, item

    def getArrays(self, chamber_number: int, row_number: int, tray_number: int, objective: int) -> list[CellModel]:
        items               : list[AlreadyScannedModel  ]       #
        models              : list[CellModel            ]       #
        focusing_status     : int                               #
        tray_model          : TrayModel                         #
        Focusing            : FocusingConfigModel               #
        Objective           : ObjectiveModel                    #
        slide_model         : ChamberStatusModel                #
        PreviewClassifier   : PreviewClassifierModel            #
        CollectClassifier   : CollectClassifierModel            #
        is_save             : int                               #
        models          = []
        focusing_status = ScanStatusLevel.Hig_First_Focus_Status
        tray_model      = TrayModel.getConfig(tray_number)
        Focusing        = FocusingConfigModel.getConfig(objective, focusing_status)
        Objective       = ObjectiveModel.getConfig(objective)
        slide_model     = self.status.getChamberSlide(chamber_number, row_number)
        if not isinstance(slide_model, SlideModelInterface) or not isinstance(Objective, ObjectiveModel) or not isinstance(tray_model, TrayModel):
            return models
        template, Preview, Collect = self.status.getSlideScanTemplate(chamber_number, row_number)
        if not isinstance(template, ScanTemplate) or not isinstance(Preview, Previewsettings) or not isinstance(Collect, Collectsettings):
            return models
        """获取当前玻片是数据模型和保存方式"""
        PreviewClassifier   = PreviewClassifierModel.Classifier(Preview.classifier)
        CollectClassifier   = CollectClassifierModel.Classifier(Collect.classifier)
        is_save             = Collect.is_save if Collect.has_is_save else 1
        """获取所有核型小图信息"""
        models = CellModel.getCellFocusingItems(tray_model, chamber_number, row_number, objective, is_save)
        if not isinstance(models, (list, tuple)) or len(models) <= 0:
            return []

        for index, item in enumerate(models):                                                                       #
            item.Level                = focusing_status                                                             #
            item.ObjectiveLevel       = objective                                                                   #
            item.ChamberGroupNumber   = chamber_number                                                              #
            item.ChamberRowNumber     = row_number                                                                  #
            item.TrayNumber           = tray_number                                                                 #
            item.LState               = slide_model.LState                                                          #
            item.RState               = slide_model.RState                                                          #
            item.IsSave               = slide_model.IsSave                                                          #
            item.CodeString           = slide_model.CodeString                                                      #
            item.SpecimenNumber       = slide_model.SpecimenNumber                                                  #
            item.SlideNumber          = slide_model.SlideNumber                                                     #
            item.LineNumber           = slide_model.LineNumber                                                      #
            item.SavePath             = Collect.save_address if Collect.has_save_address else slide_model.SavePath  #
            item.Index                = index                                                                       #
            item.SlideTable           = tray_model                                                                  #
            item.Objective            = Objective                                                                   #
            item.Focusing             = Focusing                                                                    #
            item.ScanTemplate         = template                                                                    #
            item.Preview              = Preview                                                                     #
            item.Collect              = Collect                                                                     #
            item.PreviewClassifier    = PreviewClassifier                                                           #
            item.CollectClassifier    = CollectClassifier                                                           #
            item.IsSave               = is_save                                                                     #
        return models                                                                                               #
        pass


    def finishCellFocusing(self, chamber_number: int, row_number: int) -> int:
        objective: int
        if row_number >= TrayLevel.TraySlide5:
            objective = ObjectiveLevel.Objective1
        else:
            objective = ObjectiveLevel.Objective10
        Objective : ObjectiveModel  = ObjectiveModel.getConfig(objective)
        self.microscope.stop_z()
        """初始化Z轴范围"""
        self.microscope.init_z_range()
        """设置Z轴速度"""
        self.microscope.set_z_speed(Objective.Default_Speed)
        """关闭相机"""
        self.camera.Close()

        MinRowNumber: int
        MaxRowNumber: int
        MinRowNumber, MaxRowNumber = self.system.getRowRange()
        if row_number >= MaxRowNumber:
            """获取滴油位置"""
            FrontPoint = self.getFrontOilingSlide(chamber_number, row_number, objective, False)
            self.manipulator.waitmove_xy(FrontPoint.Position_X, FrontPoint.Position_Y)
        self.microscope.waitmove_z(Objective.Safe_Limit)
        self.microscope.set_objective(objective)

        return RECODE_SUCCEED



    def initClassifier(self):
        if not self.hasAlgorithm:
            self.initAlgorithm()
        if not self.hasAlgorithm:
            return
        if not self.algorithm.hasCollectClassifier:
            self.algorithm.initCollectClassifier()



    @property
    def hasClassifier(self) -> bool:
        return self.hasAlgorithm and self.algorithm.hasCollectClassifier


    @property
    def Classifier(self) -> AlgorithmModelInterface:
        return None if not self.hasClassifier else self.algorithm.Classifier



    def getFrontOilingSlide(self, chamber_number: int, row_number: int, objective: int, isOffset: bool = True) -> SlideModelInterface:
        tray_number         : int = row_number
        FrontPoint          : SlideModelInterface                                               #
        models              : list[SlideModelInterface]                                         #
        tray_model          : TrayModel         = TrayModel.getConfig(tray_number)              #
        Anointing           : AnointingModel    = AnointingModel.getConfig(objective)           #
        OffsetRowNumber     : int = 0                                                           #
        OffsetColNumber     : int = 0                                                           #
        DropingPoints       : list[int]                                                         #
        middle              : list[list[int]]                                                   #
        _, middle = self.getOilArrays(chamber_number, row_number,tray_number, objective)
        if isOffset:
            OffsetRowNumber      = Anointing.OffsetRowNumber
            OffsetColNumber      = Anointing.OffsetColNumber
        _models: list[SlideModel] = SlideModel.getSourceOilDropingPoint(tray_model, middle, chamber_number, row_number, objective, OffsetRowNumber, OffsetColNumber)
        FrontPoint = None if _models is None or len(_models) <= 0 else _models[0]
        if isinstance(FrontPoint, SlideModelInterface):
            FrontPoint.Position_Z = Anointing.OilDaub_Position

        return FrontPoint