#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：CameraController.py
@Description：
@Author：XSH
@Time：2025/12/18 14:28
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import sys
import os
from PyQt5                                                      import Qt                                               #
from AppSettings.Settings                                       import *                                                #
from AppInterfaces.utils                                        import *                                                #
from AppCore.Levels                                             import *                                                #
from AppCore.DBModels                                           import *                                                #
from AppUtils.utils                                             import *                                                #
from AppInterfaces.UiInterface                                  import UiInterface                                      #
from AppBackEnd.AppBusinessInterface                            import AppBusinessInterface                             #
from AppHeadEnd.Commons.OperationViews.ObjectiveOperation       import ObjectiveOperation                               #
from AppHeadEnd.Commons.OperationViews.GimbalDashboardOperation import GimbalDashboardOperation                         #
from AppHeadEnd.Commons.OperationViews.DrawPositionView         import DrawPositionView                                 #
from AppHeadEnd.Commons.OperationViews.ButtonOperation          import ButtonOperation                                  #
from AppHeadEnd.Commons.OperationViews.CameraOperation          import CameraOperation, CameraAreaOperation             #
from AppHeadEnd.Commons.OperationViews.DirectionOperation       import DirectionOperation                               #
from AppHeadEnd.Commons.OperationViews.StatusOperation          import StatusOperation                                  #
from AppHeadEnd.Commons.OperationViews.FocusingOperation        import FocusingOperation                                #
from AppHeadEnd.ControllerFrames.ControlFrameInterface          import ControlFrameInterface                            #
from AppHeadEnd.ControllerFrames.FocusingController             import FocusingController, FocusingContext              #
from AppHeadEnd.DBModelViews.DBFrame                            import DBFrame                                          #
from AppUtils.HardWareUnit import HardWareUnit
# 263步是1um 263158 是1mm 131579 0.5mm 2630是10um
MIC_SAFE_POSITION: int = 263158
JOP_MOVE_VELOCITY: float = 2.0
JOP_MAX_MOVE_MIC_VELOCITY: float = 263158
JOP_MAX_MOVE_MIC_LENGTH: int = 131579
JOP_MOVE_MIC_LENGTH: int = 2630
JOP_MOVE_MIC_VELOCITY: float = 131579


class OperationController(Qt.QWidget, ControlFrameInterface):
    CameraArea                  : CameraAreaOperation               #
    Gimbal                      : GimbalDashboardOperation      #
    Direction                   : DirectionOperation            #
    Objective                   : ObjectiveOperation            #
    Position                    : DrawPositionView              #
    Buttons                     : ButtonOperation               #
    Status                      : StatusOperation               #
    Calibrate                   : FocusingController            #
    CameraTimer                 : Qt.QTimer                     #
    AxisTimer                   : Qt.QTimer                     #
    CameraInterVal              : int = 200                     #
    AxisInterVal                : int = 100                     #
    X_Max_Limit                 : int = 0                       #
    Y_Max_Limit                 : int = 0                       #
    X_Min_Limit                 : int = 0                       #
    Y_Min_Limit                 : int = 0                       #
    DBCameraView                : DBFrame                       #
    DBObjectiveView             : DBFrame                       #
    DBFocusingView              : DBFrame                       #
    DBAnointingView             : DBFrame                       #
    DBObjectiveOffsetView       : DBFrame                       #
    DBAxisView                  : DBFrame                       #
    DBChamberView               : DBFrame                       #
    DBIntegrationView           : DBFrame                       #
    DBAnalysisView              : DBFrame                       #
    DBTrayConfigView            : DBFrame                       #
    DBCellView                  : DBFrame                       #
    DBTrayView                  : DBFrame                       #

    def __init__(self, business: AppBusinessInterface, parent: Qt.QObject = None):
        super(OperationController, self).__init__(parent=parent)
        self.Business = business if isinstance(business, AppBusinessInterface) else None
        self.Initialization('OperationController', self, parent)
        # self.initWidgetStyle()
        self.SlotPositionRefreshSignal()

    def initConnect(self):
        super(OperationController, self).initConnect()
        TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
        self.CameraArea.AxisMoveSignal                          .connect(self.SlotAxisMoveSignal            , TYPE)
        self.CameraArea.MicroscopeMoveSignal                    .connect(self.SlotMicroscopeMoveSignal      , TYPE)
        self.CameraArea.MicroscopeChangeSignal                  .connect(self.SlotMicroscopeChangeSignal    , TYPE)
        self.Gimbal.AxisMoveSignal                              .connect(self.SlotAxisMoveSignal            , TYPE)
        self.Gimbal.MicroscopeMoveSignal                        .connect(self.SlotMicroscopeMoveSignal      , TYPE)
        self.Buttons.InitButton                 .clicked        .connect(self.SlotInitButton                , TYPE)
        self.Buttons.CameraOnButton             .clicked        .connect(self.SlotCameraOnButton            , TYPE)
        self.Buttons.CameraLineButton           .clicked        .connect(self.SlotCameraLineButton          , TYPE)
        self.Buttons.CameraOffButton            .clicked        .connect(self.SlotCameraOffButton           , TYPE)
        self.Buttons.InitXButton                .clicked        .connect(self.SlotInitXButton               , TYPE)
        self.Buttons.InitYButton                .clicked        .connect(self.SlotInitYButton               , TYPE)
        self.Buttons.InitMicroscopeButton       .clicked        .connect(self.SlotInitMicroscopeButton      , TYPE)
        self.Position.InfoObjective             .clicked        .connect(self.SlotInfoObjective             , TYPE)
        self.Position.InfoPositionX             .clicked        .connect(self.SlotInfoPositionX             , TYPE)
        self.Position.InfoPositionY             .clicked        .connect(self.SlotInfoPositionY             , TYPE)
        self.Position.InfoPositionZ             .clicked        .connect(self.SlotInfoPositionZ             , TYPE)
        self.Position.PositionRefreshSignal                     .connect(self.SlotPositionRefreshSignal     , TYPE)
        self.Position.UPMicMove                 .clicked        .connect(self.SlotUPMicMove                 , TYPE)
        self.Position.DownMicMove               .clicked        .connect(self.SlotDownMicMove               , TYPE)
        self.Direction.AxisMoveSignal                           .connect(self.SlotAxisDirectionSignal       , TYPE)
        self.Objective.TrayNumberChangedSignal                  .connect(self.SlotTrayNumberChangedSignal   , TYPE)
        self.Objective.ObjectiveChangedSignal                   .connect(self.SlotObjectiveChangedSignal    , TYPE)
        self.Buttons.EditPickButton             .clicked        .connect(self.SlotEditPickButton            , TYPE)
        self.Buttons.EditDropButton             .clicked        .connect(self.SlotEditDropButton            , TYPE)
        self.Buttons.ExitPlcButton              .clicked        .connect(self.SlotExitPlcButton             , TYPE)
        self.Buttons.EmptyReturnButton          .clicked        .connect(self.SlotEmptyReturnButton         , TYPE)
        self.Buttons.OilSwitchButton            .clicked        .connect(self.SlotOilSwitchButton           , TYPE)
        self.Buttons.ClearWarningButton         .clicked        .connect(self.SlotClearWarningButton        , TYPE)
        self.Buttons.LowScanButton              .clicked        .connect(self.SlotLowScanButton             , TYPE)
        self.Buttons.OilScanButton              .clicked        .connect(self.SlotOilScanButton             , TYPE)
        self.Buttons.HigScanButton              .clicked        .connect(self.SlotHigScanButton             , TYPE)
        self.Buttons.AllScanButton              .clicked        .connect(self.SlotAllScanButton             , TYPE)
        self.Buttons.PauseButton                .clicked        .connect(self.SlotPauseButton               , TYPE)
        self.Buttons.StopButton                 .clicked        .connect(self.SlotStopButton                , TYPE)
        self.Buttons.CalibrateButton            .clicked        .connect(self.SlotCalibrateButton           , TYPE)
        self.Buttons.BeginSlideButton           .clicked        .connect(self.SlotBeginSlideButton          , TYPE)
        self.Buttons.EndSlideButton             .clicked        .connect(self.SlotEndSlideButton            , TYPE)
        self.Buttons.CheckChamberButton         .clicked        .connect(self.SlotCheckChamberButton        , TYPE)
        self.Buttons.PickSlideButton            .clicked        .connect(self.SlotPickSlideButton           , TYPE)
        self.Buttons.SafeButton                 .clicked        .connect(self.SlotSafeButton                , TYPE)
        self.Buttons.InitStatusButton           .clicked        .connect(self.SlotInitStatusButton          , TYPE)
        self.Buttons.CameraButton               .clicked        .connect(self.SlotCameraButton              , TYPE)
        self.Buttons.ObjectiveButton            .clicked        .connect(self.SlotObjectiveButton           , TYPE)
        self.Buttons.FocusingButton             .clicked        .connect(self.SlotFocusingButton            , TYPE)
        self.Buttons.AnointingButton            .clicked        .connect(self.SlotAnointingButton           , TYPE)
        self.Buttons.ObjectiveOffsetButton      .clicked        .connect(self.SlotObjectiveOffsetButton     , TYPE)
        self.Buttons.AxisButton                 .clicked        .connect(self.SlotAxisButton                , TYPE)
        self.Buttons.ChamberButton              .clicked        .connect(self.SlotChamberButton             , TYPE)
        self.Buttons.IntegrationButton          .clicked        .connect(self.SlotIntegrationButton         , TYPE)
        self.Buttons.AnalysisButton             .clicked        .connect(self.SlotAnalysisButton            , TYPE)
        self.Buttons.TrayConfigButton           .clicked        .connect(self.SlotTrayConfigButton          , TYPE)
        self.Buttons.CellButton                 .clicked        .connect(self.SlotCellButton                , TYPE)
        self.Buttons.TrayButton                 .clicked        .connect(self.SlotTrayButton                , TYPE)
        self.DBCellView.ActionMoveSignal.connect(self.SlotDBViewMoveSignal, TYPE)

    def SlotDBViewMoveSignal(self, data: SlideModelInterface):
        if not isinstance(data, SlideModelInterface):
            return
        if not self.isAvailable():
            return
        objective: int              = data.ObjectiveLevel
        _objective: int             = 0
        Objective: ObjectiveModel   = ObjectiveModel.getConfig(objective)
        Tray     : TrayModel        = TrayModel.getConfig(data.TrayNumber)
        isWrong = self.microscope.waitmove(Objective.Safe_Limit)
        if RECODE_SUCCEED != isWrong:
            return
        model: CellModel = CellModel.instance(**data.to_field(True))
        x: float = round(model.Position_X, 4)
        y: float = round(model.Position_Y, 4)

        if self.CameraArea.isCrosshair:
            CellModel.setCellPoint(Tray, model)
            x: float = round(model.Position_X, 4)
            y: float = round(model.Position_Y, 4)

        isWrong = HardWareUnit.axis_waitmove_xy(self.axis_x, self.axis_y, x, y)
        if RECODE_SUCCEED != isWrong:
            return
        isWrong = self.microscope.set_objective( objective)

        if RECODE_SUCCEED != isWrong:
            AppTimeLoop.processEvents(2000)
            isWrong, _objective = self.microscope.get_objective()
            if RECODE_SUCCEED != isWrong or _objective != objective:
                return

        isWrong = self.microscope.waitmove(model.Position_Z)




    def SlotBeginSlideButton(self):
        if not self.isAvailable():
            return
        x: float
        y: float
        z: float
        isWrong, z = self.microscope.get_position()
        if RECODE_SUCCEED != isWrong or not isinstance(z, (int, float)):
            return
        isWrong, x, y = self.manipulator.position_xy()
        if RECODE_SUCCEED != isWrong or not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
            return
        slide_number: int = self.Buttons.slide_number
        if slide_number == TrayLevel.TraySlide1:
            TrayModel.changed({TrayModel.Position_X: x, TrayModel.Position_Y: y}, TrayModel.Level == slide_number)
            TrayConfigModel.setPosition_xyz(x, y, MIC_SAFE_POSITION)
        else:
            TrayModel.changed({TrayModel.Position_X: x, TrayModel.Position_Y: y}, TrayModel.Level == slide_number)

        if slide_number == TrayLevel.TraySlide2:
            Tray: TrayModel = TrayModel.getConfig(max(TrayLevel.TraySlide1, slide_number - 1))
            SlideSpacing: float = abs(Tray.Position_X - x)
            TrayConfigModel.changed({TrayConfigModel.SlideSpacing: SlideSpacing})





    def SlotEndSlideButton(self):
        if not self.isAvailable():
            return
        x: float
        y: float
        z: float
        isWrong, z = self.microscope.get_position()
        if RECODE_SUCCEED != isWrong or not isinstance(z, (int, float)):
            return
        isWrong, x, y = self.manipulator.position_xy()
        if RECODE_SUCCEED != isWrong or not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
            return
        slide_number: int = self.Buttons.slide_number
        Tray: TrayModel = TrayModel.getConfig(slide_number)
        if not isinstance(Tray, TrayModel):
            return
        xPosition   : float = Tray.Position_X
        yPosition   : float = Tray.Position_Y
        slide_width : float = abs(xPosition - x)
        slide_height: float = abs(yPosition - y)
        MaxRowNumber           = int(round(slide_width             / Tray.Step_X      , 4))
        MaxColNumber           = int(round(slide_height            / Tray.Step_Y      , 4))
        MaxRowNumber20X        = int(round(slide_width             / Tray.Step_X_20X  , 4))
        MaxColNumber20X        = int(round(slide_height            / Tray.Step_Y_20X  , 4))
        if slide_number == TrayLevel.TraySlide1:
            TrayConfigModel.changed({
                TrayConfigModel.MaxRowNumber    : MaxRowNumber          ,
                TrayConfigModel.MaxColNumber    : MaxColNumber          ,
                TrayConfigModel.MaxRowNumber20X : MaxRowNumber20X       ,
                TrayConfigModel.MaxColNumber20X : MaxColNumber20X       ,
                TrayConfigModel.SlideWidth      : slide_width           ,
                TrayConfigModel.SlideHeight     : slide_height          ,
                TrayConfigModel.Position_X      : xPosition             ,
                TrayConfigModel.Position_Y      : yPosition             ,
                TrayConfigModel.Position_Z      : MIC_SAFE_POSITION     ,
            })
        TrayModel.changed(
            {
                TrayModel.SlideWidth        : slide_width           ,
                TrayModel.SlideHeight       : slide_height          ,
                TrayModel.Position_X        : xPosition             ,
                TrayModel.Position_Y        : yPosition             ,
                TrayModel.Position_Z        : MIC_SAFE_POSITION     ,
                TrayModel.MaxRowNumber      : MaxRowNumber          ,
                TrayModel.MaxColNumber      : MaxColNumber          ,
                TrayModel.MaxRowNumber20X   : MaxRowNumber20X       ,
                TrayModel.MaxColNumber20X   : MaxColNumber20X       ,
            }
        )




    def SlotCalibrateButton(self):
        if not self.isAvailable():
            return
        self.initCalibrate()
        isVisible: bool = self.calibrate.isVisible()
        self.showCalibrate(not isVisible)

    def SlotPauseButton               (self):
        if not self.isAvailable():
            return
        isPause: bool = self.business.status.isPause
        isPause = not isPause
        self.business.status.setPause(isPause)
        if isPause:
            self.Buttons.PauseButton.setText('继续扫描')
        else:
            self.Buttons.PauseButton.setText('暂停扫描')
        pass
    def SlotStopButton                (self):
        if not self.isAvailable():
            return
        self.business.status.setExit(True)

        pass
    def SlotOilSwitchButton           (self):
        if not self.isAvailable():
            return
        if not self.hasDetection:
            return

        isON: bool = getattr(self.detection, 'OilState', False)
        status: bool = not isON
        self.detection.oil_switch(status)
        setattr(self.detection, 'OilState', status)

    def SlotClearWarningButton        (self):
        if not self.isAvailable():
            return
        self.business.clearWarning()


    def SlotLowScanButton             (self):
        if not self.isAvailable():
            return

        chamber_number  : int = self.Buttons.chamber_number
        row_number      : int = self.Buttons.slide_number
        if self.business.status.isRunning:
            return
        if self.business.status.isExit:
            self.business.status.setRunning()
        self.business.status.SetTrayDetectorStatus(chamber_number=chamber_number, status=True)
        self.business.LowScanningTraySlide(chamber_number=chamber_number, row_number=row_number)
        self.business.status.setExit()
        self.business.devices.setHeartbeat(True)
    def SlotOilScanButton             (self):
        if not self.isAvailable():
            return
        if not self.isAvailable():
            return
        chamber_number  : int = self.Buttons.chamber_number
        row_number      : int = self.Buttons.slide_number
        if self.business.status.isRunning:
            return
        if self.business.status.isExit:
            self.business.status.setRunning()
        self.business.status.SetTrayDetectorStatus(chamber_number=chamber_number, status=True)
        self.business.SlideOilDropping(chamber_number=chamber_number, row_number=row_number)
        self.business.status.setExit()
        self.business.devices.setHeartbeat(True)

    def SlotHigScanButton             (self):

        if not self.isAvailable():
            return


        chamber_number  : int = self.Buttons.chamber_number
        row_number      : int = self.Buttons.slide_number
        if self.business.status.isRunning:
            return
        if self.business.status.isExit:
            self.business.status.setRunning()
        self.business.status.SetTrayDetectorStatus(chamber_number=chamber_number, status=True)
        self.business.HigScanningTraySlide(chamber_number=chamber_number, row_number=row_number)
        self.business.status.setExit()
        self.business.devices.setHeartbeat(True)

    def SlotAllScanButton             (self):
        if not self.isAvailable():
            return
        if not self.isAvailable():
            return
        if self.business.status.isRunning:
            return

        self.business.WaitDone(isAutoScanMode=True)


    def SlotCheckChamberButton(self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        if self.business.status.isRunning:
            return
        if self.business.status.isExit:
            self.business.status.setRunning()
        self.business.devices.waitForManipulatorHome()
        self.business.status.setRunning(True)
        self.business.CheckChamberSlide(chamber_number=None, isSafe=False, isExit=False)
        self.business.status.setExit(True)

    def SlotSafeButton(self):
        if not self.isAvailable():
            return
        chamber_number: int = self.Buttons.EditTrayNumber.value()
        slide_row: int = self.Buttons.EditSlideNumber.value()
        if 1 <= chamber_number <= 24 and 1 <= slide_row <= 5:
            isWrong, objective = self.microscope.get_objective()
            if RECODE_SUCCEED != isWrong or not isinstance(objective, int):
                return
            Objective: ObjectiveModel = ObjectiveModel.getConfig(objective)
            if not isinstance(Objective, ObjectiveModel):
                return
            isWrong = self.microscope.waitmove(Objective.Safe_Limit)
            if RECODE_SUCCEED != isWrong:
                return
            Tray: TrayModel = TrayModel.getConfig(slide_row)
            if not isinstance(Tray, TrayModel):
                return
            isWrong = HardWareUnit.axis_waitmove_xy(self.axis_x, self.axis_y, Tray.Position_X, Tray.Position_Y)
            if RECODE_SUCCEED != isWrong:
                return
    def SlotInitStatusButton(self):
        from AppSettings.AppStatusSetting import AppStatus
        if not self.hasBusiness:
            self.initBusiness()
        if not self.hasBusiness:
            return
        if not self.hasManipulator:
            self.business.devices.initMotion()
            if not self.hasMotion:
                return
            self.business.devices.motion.initManipulator()
        if not self.hasCamera:
            self.business.devices.initCamera()

        if not self.hasScanCode:
            self.business.devices.initScanCode()
        if not self.hasMicroscope:
            self.business.devices.initMicroscope()
            self.business.devices.motion.initMicroscope()
        AppStatus.setManipulatorAvailable(self.hasManipulator)
        AppStatus.setTrayHAvailable(self.hasAxisH)
        AppStatus.setAxisXAvailable(self.hasAxisX)
        AppStatus.setAxisYAvailable(self.hasAxisY)
        AppStatus.setTrayZAvailable(self.hasAxisZ)
        AppStatus.setTrayHAvailable(self.hasAxisH)
        self.flushPosition()

    def flushPosition(self):
        if self.hasManipulator:
            isWrong, x, y = self.manipulator.position_xy()
            self.Position.setPosition_xy(x, y)
        if self.hasMicroscope:
            isWrong, z = self.microscope.get_position()
            self.Position.setPosition_z(z)
            isWrong, objective = self.microscope.get_objective()
            self.Position.setObjective(objective)


    def SlotPickSlideButton(self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        chamber_number: int = self.Buttons.EditTrayNumber.value()
        if 1 <= chamber_number <= 24:
            if self.business.status.isRunning:
                return
            if self.business.status.isExit:
                self.business.status.setRunning()
            self.business.devices.waitForManipulatorHome()
            self.business.status.setRunning(True)
            self.business.PickChamberSlide(chamber_number=chamber_number, isSafe=False, isExit=False, isScanCode=True)
            self.business.status.setExit(True)
        pass

    def SlotEditPickButton            (self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        chamber_number: int = self.Buttons.EditTrayNumber.value()
        if 1 <= chamber_number <= 24:
            if self.business.status.isRunning:
                return
            if self.business.status.isExit:
                self.business.status.setRunning()
            self.business.devices.waitForManipulatorHome()
            self.business.status.setRunning(True)
            self.business.PickChamberSlide(chamber_number=chamber_number, isSafe=False, isExit=False, isScanCode=False)
            self.business.status.setExit(True)
        pass

    def SlotEditDropButton            (self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        chamber_number: int = self.Buttons.EditTrayNumber.value()
        if 1 <= chamber_number <= 24:
            if self.business.status.isRunning:
                return
            if self.business.status.isExit:
                self.business.status.setRunning()
            self.business.devices.waitForManipulatorHome()
            self.business.status.setRunning(True)
            self.business.DropChamberSlide(chamber_number=chamber_number, isSafe=False, isExit=False, isScanCode=False)
            self.axis_h.wait_empty_hook_return()
            self.business.status.setExit(True)

    def SlotExitPlcButton             (self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        self.axis_h.wait_force_quit(500)


    def SlotEmptyReturnButton         (self):
        if not self.isAvailable():
            return
        if not self.hasAxisH:
            return
        self.axis_h.wait_empty_hook_return()

    def SlotAxisDirectionSignal(self, direction: int):
        if not self.isAvailable():
            return
        if not self.hasManipulator:
            return
        step: float = self.Position.movestep
        if Float(step) != float(0.0):
            if   direction == MoveDirection.Up      :
                HardWareUnit.axis_rel_move(self.axis_y, -abs(step))
            elif direction == MoveDirection.Down    :
                HardWareUnit.axis_rel_move(self.axis_y, abs(step))
            elif direction == MoveDirection.Left    :
                HardWareUnit.axis_rel_move(self.axis_x, abs(step))
            elif direction == MoveDirection.Right   :
                HardWareUnit.axis_rel_move(self.axis_x, -abs(step))
            elif direction == MoveDirection.Init:
                self.business.devices.waitForManipulatorHome()
        self.SlotRefreshPosition()

    def SlotTrayNumberChangedSignal(self, trayNumber: int):
        if not self.isAvailable():
            return
        if not self.hasMicroscope or not self.hasManipulator:
            return
        if not isinstance(trayNumber, int):
            return
        Tray: TrayModel = TrayModel.getConfig(self.Objective.traynumber)
        HardWareUnit.axis_waitmove_xy(self.axis_x, self.axis_y, Tray.Position_X, Tray.Position_Y)

    def SlotInfoObjective   (self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return
        isWrong, objective = self.microscope.get_objective()
        if RECODE_SUCCEED == isWrong and isinstance(objective, (int, float)):
            self.Position.EditObjective.setValue( int(objective))

    def SlotInfoPositionX   (self):
        if not self.isAvailable():
            return
        if not self.hasManipulator:
            return
        isWrong, position = self.axis_x.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position, (int, float)):
            self.Position.EditPositionX.setValue( round(position, 6))

    def SlotInfoPositionY   (self):
        if not self.isAvailable():
            return
        isWrong, position = self.axis_y.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position, (int, float)):
            self.Position.EditPositionY.setValue(round(position, 6))

    def SlotInfoPositionZ   (self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return
        isWrong, position = self.microscope.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position, (int, float)):
            self.Position.EditPositionZ.setValue( int(position))

    def SlotUPMicMove(self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return
        step = self.Position.getMicMoveStep()
        if step <= 0:
            return
        isWrong, position = self.microscope.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position, (int, float)):
            self.microscope.waitmove(int(step + position))
            self.Position.EditPositionZ.setValue(int(step + position))

    def SlotDownMicMove(self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return
        step = self.Position.getMicMoveStep()
        if step <= 0:
            return
        isWrong, position = self.microscope.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position, (int, float)):
            target: int = position - step
            if target < 0:
                return
            self.microscope.waitmove(target)
            self.Position.EditPositionZ.setValue(target)

    def SlotPositionRefreshSignal(self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope or not self.hasManipulator:
            return
        isWrong, objective = self.microscope.get_objective()
        if RECODE_SUCCEED == isWrong and isinstance(objective, (int, float)):
            self.Position.EditObjective.setValue(int(objective))
            if self.Objective.level != objective:
                self.Objective.setLevel(objective)

        isWrong, position_z = self.microscope.get_position()

        if RECODE_SUCCEED == isWrong and isinstance(position_z, (int, float)):
            self.Position.EditPositionZ.setValue(int(position_z))


        isWrong, position_x = self.axis_x.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position_x, (int, float)):
            self.Position.EditPositionX.setValue(round(position_x, 6))

        isWrong, position_y = self.axis_y.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position_y, (int, float)):
            self.Position.EditPositionY.setValue(round(position_y, 6))



    def SlotRefreshPosition(self):
        if not self.isAvailable():
            return
        if not self.hasMicroscope or not self.hasManipulator:
            return
        isWrong, position_x = self.axis_x.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position_x, (int, float)):
            self.Position.EditPositionX.setValue(round(position_x, 6))

        isWrong, position_y = self.axis_y.get_position()
        if RECODE_SUCCEED == isWrong and isinstance(position_y, (int, float)):
            self.Position.EditPositionY.setValue(round(position_y, 6))

    def SlotObjectiveChangedSignal(self, objective: int):
        if not self.isAvailable():
            return
        if not isinstance(objective, int):
            r_warning, level = self.microscope.get_objective()
            if RECODE_SUCCEED == r_warning:
                self.Objective.setLevel(level)
            return
        self.microscope.set_objective(objective)

    def SlotInitButton              (self):
        if not self.isAvailable():
            return
        self.business.status.setMicroscopeAvailable(False)
        self.business.devices.waitForDeviceHome()
        pass
    def SlotCameraOnButton          (self):
        if not self.isAvailable():
            return
        if self.camera_timer.isActive():
            return
        else:
            """打开相机"""
            Objective: ObjectiveModel = self.Objective.objective
            if not self.camera.IsGrabbing:
                if not self.camera.IsOpen:
                    self.camera.ReOpen(Objective.Level)
                    if not self.camera.IsOpen:
                        return


            self.microscope.changedLightIntensity(Objective.Level)
            self.camera.setModel(Objective.Level)
            self.camera.StartGrabbing()
            self.camera_timer.start()


    def SlotCameraLineButton        (self):
        if not self.hasCameraArea:
            return
        self.camera_area.IsCrosshair = not self.camera_area.IsCrosshair


    def SlotCameraOffButton         (self):
        """关闭相机"""
        if not self.isAvailable():
            return
        self.camera_timer.stop()
        self.camera_area.CameraUiSignal.emit(None, None)
        self.camera.StopGrabbing()

    def SlotCameraTimer(self):
        if not self.hasCamera:
            return
        Objective: ObjectiveModel = self.Objective.objective
        if not isinstance(Objective, ObjectiveModel):
            return
        Model: CameraModel = CameraModel.getConfig(Objective.Level)
        image: Qt.QImage = ImageUtils.cv2_to_QImage(self.camera.RequestMultipleImage(Model, Model.ExposureTime), -1, -1)
        self.camera_area.CameraUiSignal.emit(image, None)

    def SlotAxisTimer(self):
        if not self.hasManipulator:
            return
        isWrong, x, y = HardWareUnit.get_axis_position_xy(self.axis_x, self.axis_y)

        if not isinstance(x, (int, float)):
            HardWareUnit.axis_jop_stop(self.axis_x)
            return
        if not isinstance(y, (int, float)):
            HardWareUnit.axis_jop_stop(self.axis_y)
            return
        if Float(self.X_Min_Limit) > float(x):
            HardWareUnit.axis_jop_stop(self.axis_x)
        if Float(self.X_Max_Limit) < float(x):
            HardWareUnit.axis_jop_stop(self.axis_x)
        if Float(self.Y_Min_Limit) > float(y):
            HardWareUnit.axis_jop_stop(self.axis_y)
        if Float(self.Y_Max_Limit) < float(y):
            HardWareUnit.axis_jop_stop(self.axis_y)

        _, position = self.microscope.get_position()
        __, objective = self.microscope.get_objective()
        self.Position.PositionChangedSignal.emit(objective, x, y, position)


    def SlotInitXButton             (self):
        if not self.isAvailable():
            return
        self.business.status.setMicroscopeAvailable(False)
        self.business.devices.waitForManipulatorHome()


    def SlotInitYButton             (self):
        if not self.isAvailable():
            return
        self.business.status.setMicroscopeAvailable(False)
        self.business.devices.waitForManipulatorHome()

    def SlotInitMicroscopeButton    (self):
        if not self.isAvailable():
            return
        self.business.devices.waitForManipulatorHome()

    def SlotAxisMoveSignal(self, x: (float, int), y: (float, int)):
        if not self.isAvailable():
            return

        x_direction: int = MoveDirection.get_x_value(x) if isinstance(x, (float, int)) else MoveDirection.STOP
        y_direction: int = MoveDirection.get_y_value(y) if isinstance(y, (float, int)) else MoveDirection.STOP
        if isinstance(x, (int, float)) and isinstance(y, (int, float)):
            HardWareUnit.axis_jop_move_xy(self.axis_x, self.axis_y, x_direction, y_direction, JOP_MOVE_VELOCITY, JOP_MOVE_VELOCITY, self.X_Max_Limit, self.Y_Max_Limit)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        elif isinstance(x, (int, float)):
            HardWareUnit.axis_jop_move(self.axis_x, x_direction, JOP_MOVE_VELOCITY, self.X_Max_Limit)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        elif isinstance(y, (int, float)):
            HardWareUnit.axis_jop_move(self.axis_y, y_direction, JOP_MAX_MOVE_VELOCITY, self.Y_Max_Limit)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        else:
            HardWareUnit.axis_jop_stop_xy(self.axis_x, self.axis_y)
            self.axis_timer.stop()


    def SlotMicroscopeMoveSignal(self, z: (float, int)):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return

        z_direction: int = MoveDirection.get_value(z) if isinstance(z, (float, int)) else MoveDirection.STOP
        direction: int = getattr(self.microscope, 'MoveDir', None)
        JopMove  : int = getattr(self.microscope, 'JopMove', False)
        if not isinstance(direction, int):
            objective: int = self.Objective.level
            self.microscope.set_objective_limit(objective)
        else:
            if direction == z_direction:
                if z_direction == MoveDirection.STOP:
                    self.microscope.stop_z()
                    setattr(self.microscope, 'JopMove', False)
                    setattr(self.microscope, 'MoveDir', z_direction)
                    return
            if JopMove:
                self.microscope.stop_z()

        if z_direction == MoveDirection.Forward:
            self.microscope.POS_CONST_Z(JOP_MAX_MOVE_MIC_VELOCITY, abs(JOP_MAX_MOVE_MIC_LENGTH))
            setattr(self.microscope, 'JopMove', True)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        elif z_direction == MoveDirection.Backward:
            self.microscope.POS_CONST_Z(JOP_MAX_MOVE_MIC_VELOCITY, -abs(JOP_MAX_MOVE_MIC_LENGTH))
            setattr(self.microscope, 'JopMove', True)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        else:
            self.microscope.stop_z()
            self.axis_timer.stop()
            setattr(self.microscope, 'JopMove', False)
        setattr(self.microscope, 'MoveDir', z_direction)

    def SlotMicroscopeChangeSignal(self, z: (float, int)):
        if not self.isAvailable():
            return
        if not self.hasMicroscope:
            return
        step: int = max(self.Position.microscope_step, abs(JOP_MOVE_MIC_LENGTH))
        velocity: int
        Objective: ObjectiveModel = ObjectiveModel.getConfig(self.Position.objective)
        if step == abs(JOP_MOVE_MIC_LENGTH):
            velocity = JOP_MOVE_MIC_VELOCITY
        else:
            velocity = Objective.Focus_Speed
        z_direction: int = MoveDirection.get_value(z) if isinstance(z, (float, int)) else MoveDirection.STOP
        isWrong : int = self.microscope.is_moving()
        if RECODE_SUCCEED == isWrong:
            if z_direction == MoveDirection.STOP:
                self.microscope.stop_z()
                setattr(self.microscope, 'JopMove', False)
                setattr(self.microscope, 'MoveDir', z_direction)
                self.axis_timer.stop()
                self.CameraArea.Offset_Z = None
                return
        self.microscope.set_z_speed(velocity)
        if z_direction == MoveDirection.Forward:

            self.microscope.rel_move(abs( step))
            setattr(self.microscope, 'JopMove', True)
            if not self.axis_timer.isActive():
                self.axis_timer.start()

        elif z_direction == MoveDirection.Backward:
            self.microscope.rel_move(-abs( step))
            setattr(self.microscope, 'JopMove', True)
            if not self.axis_timer.isActive():
                self.axis_timer.start()
        else:
            self.microscope.stop_z()
            setattr(self.microscope, 'JopMove', False)
        setattr(self.microscope, 'MoveDir', z_direction)
        self.CameraArea.Offset_Z = None

    def SlotCameraButton              (self):
        if not self.hasDBCameraView:
            self.initDBCameraView()
        isVisible: bool = self.DBCameraView.isVisible()
        if isVisible:
            self.DBCameraView.hide()
        else:
            self.DBCameraView.show()
        pass

    def SlotObjectiveButton           (self):
        if not self.hasDBObjectiveView:
            self.initDBObjectiveView()
        isVisible: bool = self.DBObjectiveView.isVisible()
        if isVisible:
            self.DBObjectiveView.hide()
        else:
            self.DBObjectiveView.show()
        pass
    def SlotFocusingButton            (self):
        if not self.hasDBFocusingView:
            self.initDBFocusingView()
        isVisible: bool = self.DBFocusingView.isVisible()
        if isVisible:
            self.DBFocusingView.hide()
        else:
            self.DBFocusingView.show()

        pass
    def SlotAnointingButton           (self):
        if not self.hasDBAnointingView:
            self.initDBAnointingView()
        isVisible: bool = self.DBAnointingView.isVisible()
        if isVisible:
            self.DBAnointingView.hide()
        else:
            self.DBAnointingView.show()

        pass
    def SlotObjectiveOffsetButton     (self):
        if not self.hasDBObjectiveOffsetView:
            self.initDBObjectiveOffsetView()
        isVisible: bool = self.DBObjectiveOffsetView.isVisible()
        if isVisible:
            self.DBObjectiveOffsetView.hide()
        else:
            self.DBObjectiveOffsetView.show()

        pass
    def SlotAxisButton                (self):
        if not self.hasDBAxisView:
            self.initDBAxisView()
        isVisible: bool = self.DBAxisView.isVisible()
        if isVisible:
            self.DBAxisView.hide()
        else:
            self.DBAxisView.show()

        pass
    def SlotChamberButton             (self):
        if not self.hasDBChamberView:
            self.initDBChamberView()
        isVisible: bool = self.DBChamberView.isVisible()
        if isVisible:
            self.DBChamberView.hide()
        else:
            self.DBChamberView.show()

        pass
    def SlotIntegrationButton         (self):
        if not self.hasDBIntegrationView:
            self.initDBIntegrationView()
        isVisible: bool = self.DBIntegrationView.isVisible()
        if isVisible:
            self.DBIntegrationView.hide()
        else:
            self.DBIntegrationView.show()

        pass
    def SlotAnalysisButton            (self):
        if not self.hasDBAnalysisView:
            self.initDBAnalysisView()
        isVisible: bool = self.DBAnalysisView.isVisible()
        if isVisible:
            self.DBAnalysisView.hide()
        else:
            self.DBAnalysisView.show()

        pass
    def SlotTrayConfigButton          (self):
        if not self.hasDBTrayConfigView:
            self.initDBTrayConfigView()
        isVisible: bool = self.DBTrayConfigView.isVisible()
        if isVisible:
            self.DBTrayConfigView.hide()
        else:
            self.DBTrayConfigView.show()

        pass
    def SlotCellButton                (self):
        if not self.hasDBCellView:
            self.initDBCellView()
        isVisible: bool = self.DBCellView.isVisible()
        if isVisible:
            self.DBCellView.hide()
        else:
            self.DBCellView.show()


        pass
    def SlotTrayButton                (self):
        if not self.hasDBTrayView:
            self.initDBTrayView()
        isVisible: bool = self.DBTrayView.isVisible()
        if isVisible:
            self.DBTrayView.hide()
        else:
            self.DBTrayView.show()


        pass


    def initWidgetStyle(self):
        from AppUtils.UiUtils import UiUtils

    def initValue(self):
        super(OperationController, self).initValue()
        self.initCameraArea                     ()
        self.initGimbal                         ()
        self.initObjective                      ()
        self.initPosition                       ()
        self.initButtons                        ()
        self.initCameraTimer                    ()
        self.initAxisTimer                      ()
        self.initDirection                      ()
        self.initStatus                         ()
        self.initCalibrate                      ()
        self.initDBCameraView                   ()
        self.initDBObjectiveView                ()
        self.initDBFocusingView                 ()
        self.initDBAnointingView                ()
        self.initDBObjectiveOffsetView          ()
        self.initDBAxisView                     ()
        self.initDBChamberView                  ()
        self.initDBIntegrationView              ()
        self.initDBAnalysisView                 ()
        self.initDBTrayConfigView               ()
        self.initDBCellView                     ()
        self.initDBTrayView                     ()
        from AppSettings.SystemSetting import SystemConfig
        self.X_Min_Limit = 0
        self.Y_Min_Limit = 0
        self.X_Max_Limit = SystemConfig.tray.Position_X
        self.Y_Max_Limit = SystemConfig.tray.SlideHeight

    def initProperty(self):
        super(OperationController, self).initProperty()
        self.Status.start()
        # self.setMinimumHeight(self.CameraArea.height() + self.Gimbal.height())

    def show(self) -> None:

        result = super(OperationController, self).show()
        UiUtils.to_center(self)
        return result

    def initLayouts(self):
        if self.hasWindowLayouts:
            return
        main_layout: Qt.QGridLayout = Qt.QGridLayout()
        main_layout.setSpacing(2)
        main_layout.setContentsMargins(5, 5, 5, 5)

        main_layout.addWidget(self.CameraArea        , 0, 0, 1, 2)
        main_layout.addWidget(self.Objective         , 0, 2, 1, 1)
        main_layout.addWidget(self.Buttons           , 0, 3, 1, -1)
        main_layout.addWidget(self.Gimbal            , 1, 0, 1, 1)
        main_layout.addWidget(self.Position          , 1, 1, 1, 1)
        main_layout.addWidget(self.Direction         , 1, 2, 1, 1)
        main_layout.addWidget(self.Status            , 1, 3, 1, -1)
        self.setLayout(main_layout)


    def initStatus(self):
        if not self.hasStatus:
            self.Status = StatusOperation()


    def initCameraArea  (self):
        if not self.hasCameraArea  :
            self.CameraArea   = CameraAreaOperation          ()

    def initGimbal      (self):
        if not self.hasGimbal      :
            self.Gimbal       = GimbalDashboardOperation ()

    def initObjective   (self):
        if not self.hasObjective   :
            self.Objective    = ObjectiveOperation       ()

    def initPosition    (self):
        if not self.hasPosition    :
            self.Position     = DrawPositionView         ()

    def initButtons(self):
        if not self.hasButtons:
            self.Buttons = ButtonOperation()

    @property
    def camera_timer(self) -> Qt.QTimer:
        self.initCameraTimer()
        return self.CameraTimer

    @property
    def axis_timer(self) -> Qt.QTimer:
        self.initAxisTimer()
        return self.AxisTimer

    def initCameraTimer(self):
        if not self.hasCameraTimer:
            TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
            self.CameraTimer = Qt.QTimer()
            self.CameraTimer.setInterval(self.CameraInterVal)
            self.CameraTimer.timeout.connect(self.SlotCameraTimer, TYPE)

    def initAxisTimer(self):
        if not self.hasAxisTimer:
            TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
            self.AxisTimer = Qt.QTimer()
            self.AxisTimer.setInterval(self.AxisInterVal)
            self.AxisTimer.timeout.connect(self.SlotAxisTimer, TYPE)

    def initDirection(self):
        if not self.hasDirection:
            self.Direction = DirectionOperation()

    def initCalibrate(self):
        if not self.hasCalibrate:
            self.Calibrate = FocusingController()

    def showCalibrate(self, enable: bool):
        if enable:
            self.calibrate.setBusiness(self.Business)
            self.calibrate.show()
            self.calibrate.setWindowFlags(self.calibrate.windowFlags() | Qt.Qt.WindowStaysOnTopHint)
        else:
            self.calibrate.hide()

    def initDBCameraView                (self):
        if not self.hasDBCameraView                :
            self.DBCameraView                 = DBFrame(CameraModel                 (), False, '相机参数', None)
            self.DBCameraView.hide()

    def initDBObjectiveView             (self):
        if not self.hasDBObjectiveView             :
            self.DBObjectiveView              = DBFrame(ObjectiveModel              (), False, '物镜参数', None)
            self.DBObjectiveView.hide()

    def initDBFocusingView              (self):
        if not self.hasDBFocusingView              :
            self.DBFocusingView               = DBFrame(FocusingConfigModel         (), False, '定焦参数', None)
            self.DBFocusingView.hide()

    def initDBAnointingView             (self):
        if not self.hasDBAnointingView             :
            self.DBAnointingView              = DBFrame(AnointingModel              (), False, '滴油参数', None)
            self.DBAnointingView.hide()

    def initDBObjectiveOffsetView       (self):
        if not self.hasDBObjectiveOffsetView       :
            self.DBObjectiveOffsetView        = DBFrame(ObjectiveOffsetModel        (), False, '物镜偏移量参数', None)
            self.DBObjectiveOffsetView.hide()

    def initDBAxisView                  (self):
        if not self.hasDBAxisView                  :
            self.DBAxisView                   = DBFrame(AxisVelocityModel           (), False, '移动速度参数', None)
            self.DBAxisView.hide()

    def initDBChamberView               (self):
        if not self.hasDBChamberView               :
            self.DBChamberView                = DBFrame(ChamberConfigModel          (), False, '片仓参数', None)
            self.DBChamberView.hide()

    def initDBIntegrationView           (self):
        if not self.hasDBIntegrationView           :
            self.DBIntegrationView            = DBFrame(IntegrationExposureTimes    (), False, '图像预处理参数', None)
            self.DBIntegrationView.hide()

    def initDBAnalysisView              (self):
        if not self.hasDBAnalysisView              :
            self.DBAnalysisView               = DBFrame(ImageAnalysisModel          (), False, '图像预处理参数', None)
            self.DBAnalysisView.hide()

    def initDBTrayConfigView            (self):
        if not self.hasDBTrayConfigView            :
            self.DBTrayConfigView             = DBFrame(TrayConfigModel             (), False, '托盘参数', None)
            self.DBTrayConfigView.hide()

    def initDBCellView                  (self):
        if not self.hasDBCellView                  :
            self.DBCellView                   = DBFrame(CellModel                   (), True , '核型小图列表', None)

            self.DBCellView.hide()

    def initDBTrayView                  (self):
        if not self.hasDBTrayView                  :
            self.DBTrayView                   = DBFrame(TrayModel                   (), False, '托盘玻片参数', None)
            self.DBTrayView.hide()

    @property
    def hasButtons(self) -> bool:
        return self.has_field('Buttons'          , ButtonOperation         )

    @property
    def hasCameraArea(self) -> bool:
        return self.has_field('CameraArea'        , CameraAreaOperation         )

    @property
    def hasGimbal    (self) -> bool:
        return self.has_field('Gimbal'        , GimbalDashboardOperation)

    @property
    def hasObjective (self) -> bool:
        return self.has_field('Objective'     , ObjectiveOperation      )

    @property
    def hasPosition  (self) -> bool:
        return self.has_field('Position'      , DrawPositionView        )

    @property
    def hasStatus(self) -> bool:
        return self.has_field('Status'        , StatusOperation         )

    @property
    def camera_area(self) -> CameraAreaOperation:
        return None if not self.hasCameraArea else self.CameraArea



    @property
    def hasCameraFrame(self) -> bool:
        return self.hasCameraArea and self.CameraArea.hasAreaFrame

    @property
    def hasCameraTimer(self) -> bool:
        return self.has_field('CameraTimer', Qt.QTimer)

    @property
    def hasAxisTimer(self) -> bool:
        return self.has_field('AxisTimer', Qt.QTimer)

    @property
    def hasDirection(self) -> bool:
        return self.has_field('Direction', DirectionOperation)

    @property
    def hasCalibrate(self) -> bool:
        return self.has_field('Calibrate', FocusingController)














    @property
    def hasDBCameraView                (self) -> bool:
        return self.has_field('DBCameraView'           , DBFrame)

    @property
    def hasDBObjectiveView             (self) -> bool:
        return self.has_field('DBObjectiveView'        , DBFrame)

    @property
    def hasDBFocusingView              (self) -> bool:
        return self.has_field('DBFocusingView'         , DBFrame)

    @property
    def hasDBAnointingView             (self) -> bool:
        return self.has_field('DBAnointingView'        , DBFrame)

    @property
    def hasDBObjectiveOffsetView       (self) -> bool:
        return self.has_field('DBObjectiveOffsetView'  , DBFrame)

    @property
    def hasDBAxisView                  (self) -> bool:
        return self.has_field('DBAxisView'             , DBFrame)

    @property
    def hasDBChamberView               (self) -> bool:
        return self.has_field('DBChamberView'          , DBFrame)

    @property
    def hasDBIntegrationView           (self) -> bool:
        return self.has_field('DBIntegrationView'      , DBFrame)

    @property
    def hasDBAnalysisView              (self) -> bool:
        return self.has_field('DBAnalysisView'         , DBFrame)

    @property
    def hasDBTrayConfigView            (self) -> bool:
        return self.has_field('DBTrayConfigView'       , DBFrame)

    @property
    def hasDBCellView                  (self) -> bool:
        return self.has_field('DBCellView'             , DBFrame)

    @property
    def hasDBTrayView                  (self) -> bool:
        return self.has_field('DBTrayView'             , DBFrame)


    @property
    def calibrate(self) -> FocusingController:
        self.initCalibrate()
        return self.Calibrate