#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：HardWareManager.py
@Description：
@Author：XSH
@Time：2025/11/15 16:14
@Department：研发部
@Copyright：©2025 乐辰生物
"""
from PyQt5 import Qt
from PyQt5                          import Qt
from AppSettings.Settings           import *
from AppCore.Levels                 import *
from AppInterfaces.utils            import *
from AppUtils.utils                 import *
from AppInterfaces.Interfaces       import *
from AppBackEnd.BackEndEventBus.AsyncTaskController import AsyncTaskController
from AppUtils.HardWareUnit import HardWareUnit
class HardWareManager(Qt.QObject, HardWareManagerInterface, SingletonInterface):
    Locker: Qt.QMutex = Qt.QMutex()
    StatusWaitTime: int = 30000
    @classmethod
    def GetInstance(cls, *args, **kwargs) -> 'HardWareManager':
        return super(HardWareManager, cls).GetInstance(*args, **kwargs)

    def __init__(self, parent: Qt.QObject = None):
        super(HardWareManager, self).__init__(parent=parent)
        self.Initialization('HardWareManager', self, parent)




    def initValue(self):
        super(HardWareManager, self).initValue()
        self.Camera           = None
        self.Motion           = None
        self.ScanCode         = None
        self.WebServer        = None
        self.StatusTimer      = None
        self.Index            = 0
        self.Heartbeat        = True
        self.initStatusTimer()



    def initStatusTimer(self):
        if not self.hasStatusTimer:
            self.StatusTimer = Qt.QTimer()
            self.Index = 0

            TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
            self.StatusTimer.setInterval(self.StatusWaitTime)
            self.StatusTimer.timeout.connect(slot=self.SlotStatusTimerTimeOut, type=TYPE)

    
    def initCamera(self):
        if not self.hasCamera:
            from AppCore.Readers import CameraReader
            self.Camera = CameraReader.GetInstance()


    
    def initMotion(self):
        if not self.hasMotion:
            from AppCore.Readers import MotionReader
            self.Motion = MotionReader.GetInstance()


    
    def initScanCode(self):
        if not self.hasScanCode:
            from AppCore.Readers import ScanCodeReader
            self.ScanCode = ScanCodeReader.GetInstance()

    
    def initWebServer(self):
        if not self.hasWebServer:
            from AppCore.Readers import WebReader
            self.WebServer = WebReader.GetInstance()

    
    def initDevices(self):
        from AppSettings.AppStatusSetting import AppStatus

        if not self.hasCamera:
            self.initCamera()
        if not self.hasScanCode:
            self.initScanCode()
        if not self.hasWebServer:
            self.initWebServer()
        if not self.hasMotion:
            self.initMotion()


        if AppStatus.isWebSocketAvailable:
            AppStatus.setWebSocketAvailable(False)
        if AppStatus.isScanCodeAvailable:
            AppStatus.setScanCodeAvailable(False)
        if AppStatus.isMicroscopeAvailable:
            AppStatus.setMicroscopeAvailable(False)
        if AppStatus.isManipulatorAvailable:
            AppStatus.setManipulatorAvailable(False)
        if AppStatus.isBuzzerAvailable:
            AppStatus.setBuzzerAvailable(False)
        if not self.hasWebServer:
            self.initWebServer()
        if self.hasWebServer:
            if not self.webserver.IsOpen:
                self.webserver.ReOpen()
        if self.hasScanCode:
            if not self.scancode.IsOpen:
                self.scancode.ReOpen()
            if self.scancode.IsOpen:
                self.scancode.setHostMode()

        if self.hasMotion:
            if not self.motion.IsOpen:
                self.motion.initManager()
                self.manager.Open()
            if not self.hasMicroscope:
                self.motion.initMicroscope()
            if not self.microscope.isAvailable:
                self.microscope.ReOpen()


        self.UpdateDeviceStatus()

    
    def UpdateDeviceStatus(self):
        from AppSettings.AppStatusSetting import AppStatus
        AppStatus.setWebSocketAvailable         (self.isWebServerAvailable      )
        AppStatus.setCameraAvailable            (self.isCameraAvailable         )
        AppStatus.setScanCodeAvailable          (self.isScanCodeAvailable       )
        AppStatus.setMicroscopeAvailable        (self.isMicroscopeAvailable     )
        AppStatus.setManipulatorAvailable       (self.isManipulatorAvailable    )
        if AppStatus.isOilAvailable:
            if self.hasDetection:
                self.detection.oil_switch(False)

            AppStatus.setOilAvailable               (False      )
        if not AppStatus.isChamberAvailable:
            AppStatus.setChamberAvailable           (True      )
        AppStatus.waitForRunning                (                               )
        AppStatus.setScanningStatus             (ScanStatusLevel.Default_Status )
        AppStatus.setScanningDateTime(None)
        AppStatus.setExit(True)
        if self.hasDetection:
            AppStatus.setDoorAvailable(self.detection.get_door_status())
        self.StartStatusTimer()

    
    def waitForManipulatorHome(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        enable: bool = False
        waittimes: int = 50000
        isWrong: int = RECODE_FAILURE
        endtime: Qt.QDateTime

        enable = self.waitForMicroscopeHome()
        isWrong = RECODE_SUCCEED if enable else RECODE_FAILURE
        if RECODE_SUCCEED == isWrong:
            endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
            while True:
                isWrong = self.microscope.waitmove_lower_limit()
                if RECODE_SUCCEED == isWrong or Qt.QDateTime.currentDateTime() > endtime:
                    break
            """XY轴归零"""
            self.manipulator.reload_velocity()
            endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
            while True:
                isWrong = self.manipulator.waithome_xy()
                if RECODE_SUCCEED == isWrong or Qt.QDateTime.currentDateTime() > endtime:
                    break

            pass
        AppStatus.setManipulatorAvailable(isWrong == RECODE_SUCCEED)
        enable = isWrong == RECODE_SUCCEED
        return enable

    
    def waitForMicroscopeHome(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        from AppCore.AppDBModels.ConfigModels.ObjectiveModel import ObjectiveModel
        isWrong: int = RECODE_FAILURE
        Objective: ObjectiveModel
        objective: int
        position: int
        waittimes: int = 30000
        print("等待Microscope归零 1")
        endtime: Qt.QDateTime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
        if not self.hasMicroscope:
            self.initMotion()
        if self.hasMotion:
            print("等待Microscope归零", 2)
            self.microscope.stop_z()
            Objective = ObjectiveModel.getObjectiveConfig(ObjectiveLevel.Objective1)
            if not AppStatus.isMicroscopeAvailable:
                while True:
                    """清空Z轴限位"""
                    self.microscope.init_z_range()
                    """设置Z轴移动模式"""
                    self.microscope.set_z_move_mode(1)
                    """设置Z轴速度"""
                    self.microscope.set_z_speed(Objective.Default_Speed)
                    isWrong = self.microscope.waitmove_lower_limit()
                    self.microscope.stop_z()
                    if RECODE_SUCCEED == isWrong or Qt.QDateTime.currentDateTime() > endtime:
                        break
                print("等待Microscope归零", 3)
                self.microscope.waithome()
                AppStatus.setMicroscopeAvailable(True)
                print("等待Microscope归零", 4)
            else:

                """判断当前Z轴是否在安全位置"""


                endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
                position: int = None
                while True:
                    print("等待Microscope归零", 5)
                    """清空Z轴限位"""
                    self.microscope.init_z_range()
                    print("等待Microscope归零", 6)
                    """设置Z轴移动模式"""
                    self.microscope.set_z_move_mode(1)
                    print("等待Microscope归零", 7)
                    """设置Z轴速度"""
                    self.microscope.set_z_speed(Objective.Default_Speed)
                    print("等待Microscope归零", 8)
                    isWrong = self.microscope.waitmove_lower_limit()
                    print("等待Microscope归零", 9)
                    self.microscope.stop_z()
                    print("等待Microscope归零", 10)
                    while True:
                        isWrong, Moving, Lower, Upper, Limit, Focus = self.microscope.get_z_status()
                        if RECODE_SUCCEED == isWrong:
                            if Lower or Limit:
                                break
                        _, position = self.microscope.get_position()
                        if isinstance(position, (float, int)) and RECODE_SUCCEED == _:
                            if int(position) == 0:
                                break
                            else:


                                self.microscope.waitmove_lower_limit()
                                self.microscope.stop_z()
                        AppTimeLoop.processEvents(10)

                    if Qt.QDateTime.currentDateTime() > endtime:
                        break
                    isWrong, Moving, Lower, Upper, Limit, Focus = self.microscope.get_z_status()
                    if RECODE_SUCCEED == isWrong:
                        if Lower or Limit:
                            break
                    if isinstance(position, (float, int)) and int(position) == 0:
                        break
                    self.microscope.stop_z()
                    # self.microscope.initMicroscopeObjective()
                print("等待Microscope归零", 11)
            """当前物镜是否为空物镜"""
            endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
            __: int = RECODE_FAILURE
            isWrong = RECODE_FAILURE
            while True:
                isWrong = RECODE_FAILURE
                __, objective = self.microscope.get_objective()
                if RECODE_SUCCEED == __:
                    if objective == Objective.Level:
                        isWrong = RECODE_SUCCEED
                        break
                    else:

                        self.microscope.set_objective(Objective.Level)
                        AppTimeLoop.processEvents(100)
                AppTimeLoop.processEvents(200)
                if Qt.QDateTime.currentDateTime() > endtime:
                    break
            print("等待Microscope归零", 12)
        AppStatus.setMicroscopeAvailable(isWrong == RECODE_SUCCEED)
        return isWrong == RECODE_SUCCEED

    
    def waitForDetectionHome(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        enable          : bool = False
        if not self.hasMotion:
            self.initMotion()
        if self.hasDetection:
            self.detection.oil_switch   (False)
            self.detection.buzzer_switch(False)
        AppStatus.setOilAvailable       (enable)
        AppStatus.setChamberAvailable   (enable)
        AppStatus.setBuzzerAvailable    (enable)
        return self.hasDetection

    
    def waitForSlideTableHome(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        isWrong: int = self.waitForMicroscopeHome()
        waittimes: int = 50000
        endtime: Qt.QDateTime
        if RECODE_SUCCEED == isWrong:
            endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
            """XY轴归零"""
            self.manipulator.reload_axis_speed()
            while True:
                isWrong = self.manipulator.waithome_xy()
                if RECODE_SUCCEED == isWrong or Qt.QDateTime.currentDateTime() > endtime:
                    break
            endtime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
            while True:
                isWrong = self.microscope.waitmove_lower_limit()
                if RECODE_SUCCEED == isWrong or Qt.QDateTime.currentDateTime() > endtime:
                    break
            pass
        AppStatus.setManipulator(isWrong == RECODE_SUCCEED)
        return isWrong
    
    def waitForMotionHome(self) -> bool:
        """判断挂钩是否归位"""
        enable: bool = False
        try:
            endtime: Qt.QDateTime = Qt.QDateTime.currentDateTime()
            status = self.axis_h.get_backward_status()
            print("1等待Motion归零", endtime.msecsTo(Qt.QDateTime.currentDateTime()))
            if not status:
                self.axis_h.waitmove_to_back()
            print("2等待Motion归零", endtime.msecsTo(Qt.QDateTime.currentDateTime()))
            self.axis_z.waitHome()
            print("3等待Motion归零", endtime.msecsTo(Qt.QDateTime.currentDateTime()))
            enable: bool = self.waitForManipulatorHome()
            print("4等待Motion归零", endtime.msecsTo(Qt.QDateTime.currentDateTime()))
            if not enable:
                return enable
            enable = self.waitForDetectionHome()
            print("5等待Motion归零", endtime.msecsTo(Qt.QDateTime.currentDateTime()))
        except Exception as e:
            AppUtils.print_Exception(e)
        return enable

    def waitmoveForSafe(self) -> bool:
        """判断挂钩是否归位"""
        status = self.axis_h.get_backward_status()
        if not status:
            self.axis_h.waitmove_to_back()
        isWrong = self.axis_z.waitmove(0)
        # enable: bool = self.waitForManipulatorHome()
        return RECODE_SUCCEED == isWrong

    def waitForDeviceHome(self) -> bool:
        enable = self.waitForMotionHome()
        return enable



    def waitForStartSocketServer(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        enable: bool = False
        if not self.hasWebServer:
            self.initWebServer()
        if self.hasWebServer:
            if not self.webserver.IsOpen:
                self.webserver.ReOpen(1000)
            enable = self.webserver.IsOpen
        AppStatus.setWebSocketAvailable(enable)
        return enable

    
    def waitForCamera(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        enable: bool = False
        if not self.hasCamera:
            self.initCamera()
        if self.hasCamera:
            if not self.camera.IsOpen:
                self.camera.ReOpen(ObjectiveLevel.ObjectiveNone)
            enable = self.camera.IsOpen
        AppStatus.setCameraAvailable(enable)
        return enable

    
    def waitForScanCode(self) -> bool:
        from AppSettings.AppStatusSetting import AppStatus
        enable: bool = False
        if not self.hasScanCode:
            self.initScanCode()
        if self.hasScanCode:
            self.scancode.setHostMode()
            self.scancode.StartScanningCode()
            self.scancode.ReadScanningCode(500)
        enable = self.hasScanCode and self.scancode.IsOpen
        AppStatus.setScanCodeAvailable(enable)
        return enable

    

    
    def getCurrentObjective(self, maxcount: int = 3) -> (int, int):
        isWrong: int = RECODE_FAILURE
        objective: int = ObjectiveLevel.Objective1
        if not self.hasMicroscope:
            return RECODE_FAILURE, objective
        max_count: int = min(max(1, maxcount), 10)
        for count in range(max_count):
            isWrong, objective = self.microscope.currentObjective()
            if RECODE_SUCCEED == isWrong:
                break
        return isWrong, objective

    
    def changedObjective(self, level: int, is_lower_limit: bool = True, maxcount: int = 3) -> int:
        isWrong: int = RECODE_FAILURE
        if not self.hasMicroscope:
            return RECODE_FAILURE
        max_count: int = min(max(1, maxcount), 10)
        for count in range(max_count):
            isWrong = self.microscope.changedObjective(level, is_lower_limit)
            if RECODE_SUCCEED == isWrong:
                break
        return isWrong

    
    def changedOilStatus(self, enable: (bool, int), maxcount: int = 3) -> int:
        isWrong: int = RECODE_FAILURE
        if not self.hasDetection:
            return RECODE_FAILURE
        max_count: int = min(max(1, maxcount), 10)
        for count in range(max_count):
            isWrong = self.detection.oil_switch(enable)
            # print("油泵开关状态", self.detection.get_oil_status())
            if RECODE_SUCCEED == isWrong:
                break
        return isWrong




    def SlotStatusTimerTimeOut(self):
        if not self.hasIndex:
            self.Index = 0
        index: int = self.Index % 5
        self.Index = index + 1
        self.RefreshStatus(index)
        try:
            if index == 0:
                if not self.hasScanCode:
                    self.initScanCode()
                if not self.scancode.IsOpen:
                    self.scancode.manager.deleManager()
                    self.scancode.manager.initManager()
                    self.scancode.manager.ReOpen()
                    self.scancode.ReOpen()
            elif index == 1:
                if self.hasMotion:
                    if not self.hasMicroscope:
                        self.motion.initMicroscope()
                    if not self.microscope.isAvailable:
                        self.microscope.ReOpen()
            elif index == 2:
                if not self.hasMotion:
                    self.initMotion()
                if self.hasMotion:
                    if not self.motion.hasManager:
                        self.motion.initManager()
            elif index == 3:
                if not self.hasCamera:
                    self.initCamera()
                if not self.camera.isAvailable:
                    self.camera.ReOpen(ObjectiveLevel.Objective1)
                # if not self.camera.IsOpen:
                #     self.camera.ReOpen(ObjectiveLevel.ObjectiveNone)
            elif index == 4:
                if not self.hasWebServer:
                    self.initWebServer()
                if not self.webserver.IsOpen:
                    self.webserver.ReOpen()


        except Exception as e:
            from AppLog.logHelper import logMANIPULATOR
            error_string: str = AppUtils.toString(e)
            logMANIPULATOR << error_string



    async def async_RefreshStatus(self, index: int):
        if not self.hasMotion:
            self.initMotion()
        if not self.hasManagerPool:
            if self.hasMotion:
                self.motion.initManager()
        isWrong : int                                       #
        x       : float                                     #
        y       : float                                     #
        z       : float                                     #
        buzzer  : bool                                      #
        isWrong, x = self.manipulator.axis_x.get_position   ()
        isWrong, y = self.manipulator.axis_y.get_position   ()
        isWrong, z = self.manipulator.axis_z.get_position   ()
        buzzer     = self.detection.get_buzzer_switch       ()
        print(f'当前模块状态: 序号{index} 轴位置: x:{x} y:{y} z:{z} 蜂鸣器:{buzzer}')

    def RefreshStatus(self, index: int):
        if not self.hasMotion:
            self.initMotion()
        if not self.hasManagerPool:
            if self.hasMotion:
                self.motion.initManager()
        from AppSettings.AppStatusSetting import AppStatus
        isWrong : int                                       #
        x       : float                                     = None#
        y       : float                                     = None#
        z       : float                                     #
        buzzer  : bool                                      #
        Number  : int = getattr(self, 'Number', 0)
        maxLength: int = self.manipulator.manager.length()
        Number  = (Number + 1) % maxLength
        handler: ModBusImplInterface
        if self.isHeartbeat:
            handler = self.manipulator.manager[Number]
        else:
            if AppStatus.isPause:
                handler = self.manipulator.scan_handler
            else:
                handler = self.manipulator.readhandler
        try:
            if not handler.isAvailable:
                handler.ReConnected()
            x       = HardWareUnit.ReadFloat    (handler                    , self.manipulator.axis_x.Parameter.CurrentPosition         )
            y       = HardWareUnit.ReadFloat    (handler                    , self.manipulator.axis_y.Parameter.CurrentPosition         )
            z       = HardWareUnit.ReadFloat    (handler                    , self.manipulator.axis_z.Parameter.CurrentPosition         )
            h       = HardWareUnit.ReadUInt16   (self.axis_h.hook_handler   , self.manipulator.axis_h.Parameter.SetLayerCountRegister   )
            buzzer  = HardWareUnit.ReadBool     (handler                    , self.manipulator.detector.Parameter.BuzzerRegister        )
            print(f"index: {index} X: {x} Y: : {y} Z: {z} Buzzer: {buzzer} Layer: {h} ")
        except Exception as e:
            AppUtils.print_Exception(e)
        setattr(self, 'Number', Number)


    async def async_SlotStatusTimerTimeOut(self):
        if not self.hasIndex:
            self.Index = 0
        index: int = self.Index % 7
        self.Index += 1

        try:
            if not self.hasMotion:
                self.initMotion()
            if not self.hasManagerPool:
                if self.hasMotion:
                    self.motion.initManager()

            isWrong : int           #
            x       : float         #
            y       : float         #
            z       : float         #
            buzzer  : bool          #
            isWrong, x = self.manipulator.axis_x.get_position()
            isWrong, y = self.manipulator.axis_x.get_position()
            isWrong, z = self.manipulator.axis_x.get_position()
            buzzer     = self.detection.get_buzzer_switch()
            print("X:", x, "Y:", y, "Z:", z, "Buzzer:", buzzer)

            if index == 1:
                if not self.hasScanCode:
                    self.initScanCode()
                if not self.scancode.IsOpen:
                    self.scancode.manager.deleManager()
                    self.scancode.manager.initManager()
                    self.scancode.manager.ReOpen()
                    self.scancode.ReOpen()
            elif index == 2:
                if self.hasMotion:
                    if not self.hasMicroscope:
                        self.motion.initMicroscope()
                        self.microscope.ReOpen()
            elif index == 3:
                if not self.hasMotion:
                    self.initMotion()
                if self.hasMotion:
                    if not self.motion.hasManager:
                        self.motion.initManager()
            elif index == 4:
                if not self.hasCamera:
                    self.initCamera()
                if not self.camera.IsOpen:
                    self.camera.ReOpen(ObjectiveLevel.ObjectiveNone)
            elif index == 5:
                if not self.hasDetection:
                    self.initDetection()
                self.detection.get_buzzer_switch()
            elif index == 6:
                if not self.hasWebServer:
                    self.initWebServer()
                if not self.webserver.IsOpen:
                    self.webserver.ReOpen()


        except Exception as e:
            from AppLog.logHelper import logMANIPULATOR
            error_string: str = AppUtils.toString(e)
            logMANIPULATOR << error_string