#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：ChamberPickBusiness.py
@Description：
@Author：XSH
@Time：2025/11/15 13:04
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import asyncio
import copy
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
class ChamberPickBusiness(Qt.QObject, AppBusinessController):
    def __init__(self, parent: AppBusinessInterface = None):
        super(ChamberPickBusiness, self).__init__(parent=parent if parent is not None else parent.m_Control)
        self.initialize()
        self.initScanLevel()
        self.initModules(parent)

    def initScanLevel(self):
        self.ScanLevel = ScanStatusLevel.PickChamberSlide_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.hasAxisZ and self.hasMicroscope and self.hasAxisH

    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 )
            tray_number     : int   = AppConvert.ObjectConvert(args.get('tray_number'     , None), int )
            isExit          : bool  = AppConvert.ObjectConvert(args.get('isExit'          , False), bool)
            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
                elif key == 'tray_number':
                    pass
                elif key == 'isExit':
                    pass
                else:
                    _args[key] = args.get(key, None)
            if isinstance(chamber_number, int):
                self.changedTrayScanningStatus(chamber_number, self.ScanLevel, ObjectiveLevel.Objective1)
                isWrong = self.PickChamberTray(chamber_number=chamber_number, isSafe=isSafe, isExit=isExit, **_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)
        elif not ScanStatusLevel.isHitBotWarning(self.status.scanning_level):
            self.status.setScanningStatus(ScanStatusLevel.Check_Warning_Status)
        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 )
            tray_number     : int   = AppConvert.ObjectConvert(args.get('tray_number'     , None), int )
            isExit          : bool  = AppConvert.ObjectConvert(args.get('isExit'          , False), bool)
            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
                elif key == 'tray_number':
                    pass
                elif key == 'isExit':
                    pass
                else:
                    _args[key] = args.get(key, None)
            if isinstance(chamber_number, int):
                self.ChangedScanningStatus(chamber_number, 1, self.ScanLevel)
                self.ChangedScanningStatus(chamber_number, 2, self.ScanLevel)
                self.ChangedScanningStatus(chamber_number, 3, self.ScanLevel)
                self.ChangedScanningStatus(chamber_number, 4, self.ScanLevel)
                self.ChangedScanningStatus(chamber_number, 5, self.ScanLevel)
                isWrong = self.PickChamberTray(chamber_number=chamber_number, isSafe=isSafe, isExit=isExit, **_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 PickChamberTray(self, chamber_number: int, isSafe: bool = False,isExit: bool = True, **args) -> int:
        isWrong: int = RECODE_FAILURE
        MaxChamberNumber: int
        MinChamberNumber: int
        MinRowNumber: int
        MaxRowNumber: int
        MinRowNumber, MaxRowNumber = self.system.getRowRange()
        MinChamberNumber, MaxChamberNumber = self.system.getChamberRange()
        if not isinstance(chamber_number, (int, float)) or int(chamber_number) < MinChamberNumber or int(chamber_number) > MaxChamberNumber:
            return isWrong
        if not self.isAvailable:
            return isWrong
        isWrong = self.readly_pick_chamber_tray(chamber_number)
        if RECODE_SUCCEED != isWrong:
            return isWrong
        if isExit:
            """处理扫描状态: 暂停 退出 跳过"""
            SCANNING_STATUS = self.process_scanning_status()
            if SCANNING_STATUS == RECODE_CHANGED:
                return RECODE_SUCCEED
        """强制退出"""
        isWrong = self.axis_h.wait_force_quit(500)
        if RECODE_SUCCEED != isWrong:
            return isWrong
        isScanCode: bool = args.get('isScanCode', True)
        if isScanCode:
            count: int = 0
            while count < 3:
                try:
                    isWrong = self.axis_h.waitmove_scan_code_position(chamber_number)
                except Exception as e:
                    AppTimeLoop.processEvents(2000)
                    continue
                AppTimeLoop.processEvents(100)
                if RECODE_SUCCEED == isWrong:
                    break
                count += 1
            AppTimeLoop.processEvents(100)
            row_number: int = MaxRowNumber
            if RECODE_SUCCEED != isWrong:
                isWrong = self.process_pick_warning()
                self.axis_h.wait_force_quit(500)
                return isWrong
            while row_number >= MinRowNumber:
                """处理暂停"""
                self.process_scanning_status()
                """判断是否选择扫描此玻片号 如果选择扫描才扫码 """
                if RECODE_SUCCEED == self.status.isPickAvailable(chamber_number, row_number):
                    self.waitForScanCodeOfTray(chamber_number, row_number)
                    AppTimeLoop.processEvents(100)

                scan_code_count: int = 0
                while scan_code_count < 3:
                    isWrong = self.axis_h.waitmove_scan_code_complete()
                    AppTimeLoop.processEvents(100)
                    if RECODE_SUCCEED == isWrong:
                        break
                    scan_code_count += 1
                if RECODE_SUCCEED != isWrong:
                    isWrong = self.axis_h.reset_hook()
                    continue
                row_number -= 1
        else:
            isWrong = self.axis_h.wait_pick_tray(chamber_number)
        """如果扫码超时"""
        isWrong = self.axis_h.waitForScanCodePickComplete()
        if RECODE_SUCCEED != isWrong:
            isWrong = self.process_pick_warning()
            self.axis_h.wait_force_quit(500)
            self.process_scanning_warning("取托盘错误", True)
        return isWrong



    def readly_pick_chamber_tray(self, chamber_number: int) -> int:
        isWrong: int = RECODE_FAILURE
        if not self.isAvailable:
            return isWrong
        isWarning, _chamber_number = self.waitForSucceedPickAvailable(chamber_number=chamber_number)
        if RECODE_SUCCEED != isWarning or chamber_number != _chamber_number:
            return isWrong
        from AppCore.AppDBModels.DBModels import ChamberConfigModel, ObjectiveModel
        x: float
        y: float
        z: float
        objective: int = ObjectiveLevel.Objective1
        x, y, z = ChamberConfigModel.getCheckPosition_xyz()
        Objective: ObjectiveModel = ObjectiveModel.getConfig(objective)
        self.microscope.init_z_range()
        self.microscope.set_z_speed(Objective.Default_Speed)
        isWrong = self.microscope.waitmove(z)
        if RECODE_SUCCEED != isWrong:
            return isWrong
        isWrong = self.microscope.set_objective(objective)
        return isWrong


    def waitForScanCodeOfTray(self, chamber_number: int, row_number: int):
        Number: int
        isNumber: bool = False
        endtime: Qt.QDateTime = Qt.QDateTime.currentDateTime().addMSecs(6000)
        while True:
            CodeString: str = self.scancode.ReadScanningCode()
            if not isinstance(CodeString, str) or len(CodeString) <= 0:
                CodeString = self.scancode.ReadScanningCode()
            if isinstance(CodeString, str) and len(CodeString) > 0:
                try:
                    Number = int(CodeString)
                except Exception as e:
                    Number = None
                isNumber = isinstance(Number, int)
                if not isNumber:
                    break
            if Qt.QDateTime.currentDateTime() > endtime:
                CodeString = None
                break
        if isinstance(CodeString, str) and len(CodeString) > 0:
            CodeString = CodeString.replace('\r\n', '')
            CodeString = CodeString.replace('\n\r', '')

        self.SetChamberCodeString(chamber_number, row_number, CodeString)

    def SetChamberCodeString(self, ChamberNumber: int, RowNumber: int, CodeString: str):
        from AppCore.AppDBModels.DBModels import CustomScanningModel, ChamberStatusModel, ChamberConfigModel, ScanTemplate, PreviewClassifierModel, CollectClassifierModel
        SpecimenNumber  : str = None
        SlideNumber     : str = None
        CustomCodeString: str = None
        from AppLog.logHelper import logSERIAL
        if not isinstance(CodeString, str) or len(CodeString) <= 0:
            logSERIAL << f'{ChamberNumber}号片仓{RowNumber}号玻片匹配样本号错误 {CodeString}'
            return CustomCodeString, SpecimenNumber, SlideNumber
        try:
            Template: ScanTemplate
            """判断当前玻片是否为自定义模板"""
            IsAutoScanCode = CustomScanningModel.IsAutoScanCode(ChamberNumber, RowNumber)
            if IsAutoScanCode:
                """自动匹配模板"""
                Template = ScanTemplate.getTemplate(CodeString)
            else:
                """自定义模板"""
                Template = CustomScanningModel.getTemplate(ChamberNumber, RowNumber)
            if Template is None:
                if IsAutoScanCode:
                    logSERIAL << f'{ChamberNumber}号片仓{RowNumber}号玻片样本号{CodeString}自动匹配模板失败'
                else:
                    logSERIAL << f'{ChamberNumber}号片仓{RowNumber}号玻片样本号{CodeString}没有选择自定义模板'
                return CustomCodeString, SpecimenNumber, SlideNumber

            separator_no: str = f'{Template.separator_no}'
            specimen: str

            if isinstance(CodeString, str) and len(CodeString) > 0:
                CustomCodeString = f'{CodeString}'
            else:
                if not IsAutoScanCode:
                    CustomSpecimen: str = CustomScanningModel.getCustomSpecimen(ChamberNumber, RowNumber)
                    CustomCodeString = CustomSpecimen if isinstance(CustomSpecimen, str) and len(CustomSpecimen > 0) else None

            if isinstance(CustomCodeString, str) and len(CustomCodeString) > 0:
                """根据模板解析出样本号 玻片号"""
                Buffer: list[str] = CustomCodeString.split(separator_no)
                SlideNumber = f'{Buffer[-1]}' if len(Buffer) > 1 else None
                SlideNumber_Number = AppConvert.ObjectConvert(SlideNumber, int)
                SpecimenNumber = ''
                """玻片号为二维码最后一位 如果二维码不能分割 则玻片号为行号"""
                """样本号为 二维码号中除了玻片号的字符串"""
                if SlideNumber_Number is None:
                    SlideNumber = f'{RowNumber}'
                    SpecimenNumber = f'{CustomCodeString}'
                elif len(Buffer) == 1:
                    SlideNumber = f'{RowNumber}'
                    SpecimenNumber = f'{Buffer[0]}'
                else:
                    SlideNumber = f'{SlideNumber_Number}'
                    maxlength: int = len(Buffer) - 1
                    for index in range(0, maxlength):
                        if index == maxlength - 1:
                            SpecimenNumber += f'{Buffer[index]}'
                        else:
                            SpecimenNumber += f'{Buffer[index]}{separator_no}'

            logSERIAL << f"解析样本号: {CustomCodeString}  {SpecimenNumber} {SlideNumber}"
            CustomScanningModel.setChamberCodeString(ChamberNumber, RowNumber, CustomCodeString, SpecimenNumber, SlideNumber)
            ChamberStatusModel.setChamberCodeString(ChamberNumber, RowNumber, CustomCodeString, SpecimenNumber, SlideNumber)
            """如果是自动匹配模板 更新模板ID"""
            if IsAutoScanCode:
                CustomScanningModel.set_template(chamber_number=ChamberNumber, row_number=RowNumber,
                                                 template_ID=Template.ID, preview_ID=Template.previewsettings_ID,
                                                 collect_ID=Template.collectsettings_ID)
        except Exception as e:
            AppUtils.print_Exception(e)
            SpecimenNumber  = None
            SlideNumber     = None

        return CustomCodeString, SpecimenNumber, SlideNumber