#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：ModBusImpl.py
@Description：
@Author：XSH
@Time：2025/11/11 19:54
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import os
import sys
import clr
import socket
import gc
import struct
from clr_loader.types                           import Assembly, ClrFunction    #
from PyQt5                                      import Qt                       #
from abc                                        import abstractmethod           #
from AppUtils.utils                             import *                        #
from AppSettings.Settings                       import *                        #
from AppContexts.Contexts                       import ModBusContext, ModBusRegister
from AppInterfaces.ImplInterfaces               import ModBusImplInterface
MODBUS_UNIT: int = 1

class ModBusImpl(Qt.QObject, ModBusImplInterface):
    IOLocker: Qt.QMutex

    def __init__(self, context: ModBusContext = None, parent: Qt.QObject = None):
        super(ModBusImpl, self).__init__(parent=parent)
        self.Parameter = context
        self.Initialization("ModBusImpl", self, parent)

    def isValid(self) -> bool:
        return self.hasManager and self.hasParameter

    @property
    def hasIOLocker(self) -> bool:
        return hasattr(self, 'IOLocker') and self.IOLocker is not None and isinstance(self.IOLocker, Qt.QMutex)

    def initIOLocker(self):
        if not self.hasIOLocker:
            self.IOLocker = Qt.QMutex()

    @property
    def isAvailable(self) -> bool:
        return self.hasManager and self.IsOpen()

    @classmethod
    def instance(cls, context: ModBusContext = None, parent: Qt.QObject = None) -> 'ModBusImpl':
        result = cls.__new__(cls)
        result.__init__(context=context, parent=parent)
        # result.Open()
        return result

    def setParameter(self, context: ModBusContext):
        if self.Parameter.isEqual(context):
            return
        isOpen: bool = self.IsOpen()
        self.deleManager()
        self.Parameter = context
        self.initManager(self.Parameter)
        if isOpen:
            self.Open()

    def IsOpen(self) -> bool:
        try:
            return self.isValid() and self.Manager.IsOpen
        except Exception as e:
            return False

    def isEnabled(self) -> bool:
        return self.isValid() and self.Manager.IsAvailable

    def isDisable(self) -> bool:
        return not self.isEnabled()

    def Open(self, timeOut: int = 1000, **args) -> int:
        if not self.isValid():
            self.initManager(self.Parameter)
        if not self.isValid():
            return RECODE_FAILURE
        if self.isEnabled():
            self.Close(timeOut)
        self.Parameter.IsOpen = False
        try:
            self.Manager.setParameter(self.Parameter.Addr, self.Parameter.Port, 1000, 1000, MODBUS_UNIT)
            self.Manager.Open()
            self.Parameter.IsOpen = self.Manager.IsAvailable
        except Exception as e:
            AppUtils.print_Exception(e)
        return RECODE_SUCCEED if self.Parameter.IsOpen else RECODE_FAILURE

    def Close(self, timeOut: int = 1000, **args) -> int:
        if not self.isValid():
            return RECODE_SUCCEED
        try:
            self.Manager.Close()
        except Exception as e:
            AppUtils.print_Exception(e)

        self.Parameter.IsOpen = False
        return RECODE_SUCCEED if not self.Parameter.IsOpen else RECODE_FAILURE

    def initManager(self, context: ModBusContext = None, **args):
        self.initContext()
        if not self.hasManager:
            try:
                filename: str = self.Parameter.absoluteFilePath
                info: Qt.QFileInfo = Qt.QFileInfo(filename)
                if not info.exists() or not Qt.QLibrary.isLibrary(info.absoluteFilePath()):
                    return
                sys.path.append(info.absolutePath())
                # sys.path.append(info.absoluteFilePath())
                clr.AddReference(info.absoluteFilePath())
                clr.AddReference('System')
                from ModBusAPI import YQLModBus, YQLMController
                self.Manager: YQLModBus = YQLModBus()
                self.Controller: YQLMController = YQLMController()
            except Exception as e:
                AppUtils.print_Exception(e)
                self.Manager = None

    def deleManager(self):
        if self.hasManager:
            try:
                self.Manager.Close()
                self.Manager.Dispose()
            except Exception as e:
                AppUtils.print_Exception(e)
            self.Manager = None
            self.Parameter.IsOpen = False

    def initContext(self):
        if not self.hasParameter:
            self.Parameter = ModBusContext()
            try:
                from AppSettings.AppSetting import AppConfig
                self.Parameter.deserialize(AppConfig.ManipulatorConfig.ModBusConfig.serialize())
            except Exception as e:
                pass

    def ReConnected(self):
        try:
            if not self.hasManager:
                self.initManager()
                if not self.hasManager:
                    return
                self.ReOpen()
                return
            self.Manager.ReOpen()
            self.parameter.IsOpen = self.Manager.IsAvailable
        except Exception as e:
            AppUtils.print_Exception(e)

    @log_exceptions
    def ReadRegister(self, address: int):
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadUInt16(self.Manager, address)
                if isinstance(result, (bool, int)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadRegisters(self, address: int, Count: int, values: list[int]) -> int:
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            count: int = max(1, Count)
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadUInt16s(self.Manager, address, count)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadCoil(self, address: int) -> int:
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadBool(self.Manager, address)
                if isinstance(result, (bool, int)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadFloat(self, address: int) -> float:
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadFloat(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadInt32(self, address: int):
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadInt32(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadUInt32(self, address: int):
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadUInt32(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadInt16(self, address: int):
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadInt16(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadUInt16(self, address: int):
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadUInt16(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadState(self, address: int) -> int:
        result: int = None
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return result
            for index in range(self.ReConnectedNumber):
                result = self.Controller.ReadBool(self.Manager, address)
                if isinstance(result, (int, float)):
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return result

    def ReadCoils(self, address: int, Count: int, values: list[bool]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            count: int = max(1, Count)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.ReadBools(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([data for data in buffer])
                    break

            pass
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadFloats(self, address: int, Count: int, values: list[float]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            count: int = max(2, Count * 2)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.ReadFloats(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([round(float(data), 4) for data in buffer])
                    break
            pass
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadInt32s(self, address: int, Count: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            count: int = max(1, Count)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.Readint32s(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([int(data) for data in buffer])
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadUInt32s(self, address: int, Count: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            count = max(1, Count)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.ReadUInt32s(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([int(data) for data in buffer])
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadUInt16s(self, address: int, Count: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            count: int = max(1, Count)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.ReadUInt16s(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([int(data) for data in buffer])
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadInt16s(self, address: int, Count: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            count: int = max(1, Count)
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            for index in range(self.ReConnectedNumber):
                buffer = self.Controller.ReadInt16s(self.Manager, address, count)
                if buffer is not None:
                    values.clear()
                    values.extend([int(data) for data in buffer])
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def ReadStatus(self, address: int, Count: int, values: list[bool]) -> int:
        return self.ReadCoils(address, Count, values)

    def SendCoils(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            import System
            VALUES: list[int] = [True if item else False for item in values if isinstance(item, (bool, int))]
            if len(VALUES) <= 0:
                return isWarning
            array = System.Array[System.Boolean](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendBools(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendFloats(self, address: int, values: list[float]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning

            import System
            array: list[float] = [float(item) for item in values if isinstance(item, (int, float))]
            float_array = System.Array[System.Single](array)
            if len(float_array) <= 0:
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendFloats(self.Manager, int(address), float_array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendInt32s(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            VALUES: list[int] = [int(item) for item in values if isinstance(item, (int, float))]
            if len(VALUES) <= 0:
                return isWarning
            import System
            array = System.Array[System.Int32](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendInt32s(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendUInt32s(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            VALUES: list[int] = [int(item) for item in values if isinstance(item, (int, float))]
            if len(VALUES) <= 0:
                return isWarning
            import System
            array = System.Array[System.UInt32](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendUInt32s(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendInt16s(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            VALUES: list[int] = [int(item) for item in values if isinstance(item, (int, float))]
            if len(VALUES) <= 0:
                return isWarning
            import System
            array = System.Array[System.Int16](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendInt16s(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendUInt16s(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            VALUES: list[int] = [int(item) for item in values if isinstance(item, (int, float))]
            if len(VALUES) <= 0:
                return isWarning
            import System
            array = System.Array[System.UInt16](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendUInt16s(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendStatus(self, address: int, values: list[int]) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            import System
            VALUES: list[int] = [True if item else False for item in values if isinstance(item, (int, bool))]
            array = System.Array[System.Boolean](VALUES)
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendBools(self.Manager, address, array)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendCoil(self, address: int, value: bool) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, bool)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendBool(self.Manager, address, value)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendFloat(self, address: int, value: float) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, float)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendFloat(self.Manager, address, float(value))
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendInt32(self, address: int, value: int) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, float)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendInt32(self.Manager, address, int(value))
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendUInt32(self, address: int, value: int) -> int:
        isWarning: int = RECODE_FAILURE
        try:

            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, float)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendUInt32(self.Manager, address, int(value))
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendInt16(self, address: int, value: int) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, float)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendInt16(self.Manager, address, int(value))
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendUInt16(self, address: int, value: int) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, float)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendUInt16(self.Manager, address, int(value))
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

    def SendState(self, address: int, value: bool) -> int:
        isWarning: int = RECODE_FAILURE
        try:
            if not self.is_socket_connected:
                self.ReConnected()
            if not self.is_socket_connected:
                return isWarning
            if not isinstance(value, (int, bool)):
                return isWarning
            for index in range(self.ReConnectedNumber):
                isWarning = self.Controller.SendBool(self.Manager, address, True if isinstance(value, (int, bool)) and value else False)
                if isWarning == RECODE_SUCCEED:
                    break
        except Exception as e:
            AppUtils.print_Exception(e)
        return isWarning

