import time
import os
import sys
from ctypes import *
import ctypes
import glob

# ch341类：iic读写，int引脚读写，速度设置
class ClassCh341:
    # 接口固定宏
    _mCH341A_CMD_I2C_STREAM = 0xAA       # I2C接口的命令包,从次字节开始为I2C命令流
    _mCH341A_CMD_I2C_STM_STA = 0x74	    # I2C接口的命令流:产生起始位
    _mCH341A_CMD_I2C_STM_STO = 0x75		# I2C接口的命令流:产生停止位
    _mCH341A_CMD_I2C_STM_OUT = 0x80		# I2C接口的命令流:输出数据,位5-位0为长度,后续字节为数据,0长度则只发送一个字节并返回应答
    _mCH341A_CMD_I2C_STM_IN = 0xC0		# I2C接口的命令流:输入数据,位5-位0为长度,0长度则只接收一个字节并发送无应答
    _mCH341A_CMD_I2C_STM_MAX = 63        # I2C接口的命令流单个命令输入输出数据的最大长度
    _mCH341A_CMD_I2C_STM_SET = 0x60		# I2C接口的命令流:设置参数,位2=SPI的I/O数(0=单入单出,1=双入双出),位1位0=I2C速度(00=低速,01=标准,10=快速,11=高速)
    _mCH341A_CMD_I2C_STM_US = 0x40		# I2C接口的命令流:以微秒为单位延时,位3-位0为延时值
    _mCH341A_CMD_I2C_STM_MS = 0x50		# I2C接口的命令流:以亳秒为单位延时,位3-位0为延时值
    _mCH341A_CMD_I2C_STM_DLY = 0x0F		# I2C接口的命令流单个命令延时的最大值
    _mCH341A_CMD_I2C_STM_END = 0x00		# I2C接口的命令流:命令包提前结束

    _mStateBitINT = 0x00000400

    IIC_SPEED_20 = 0
    IIC_SPEED_100 = 1
    IIC_SPEED_400 = 2
    IIC_SPEED_750 = 3

    def __init__(self):
        self.deviceID = ctypes.c_uint32()
        # A sensor may be optional for motor-only programs.  Keep an explicit
        # unopened state so cleanup does not attempt to close an invalid fd.
        self.fd = None

    def init(self):
        if os.name == 'nt':  # Windows 环境
            libPath = os.path.dirname(sys.argv[0]) + r'/lib/ch341/CH341DLLA64.DLL'
        elif os.name == 'posix':
            libPath = './lib/ch341/libch347.so'
        dllExist = os.path.exists(libPath)
        if not dllExist:
            print('未找到库文件')
            return False
        else:
            try:
                if os.name == 'nt':  # Windows 环境
                    self.ic = windll.LoadLibrary(libPath)  # ch341接口  

                    self.ch341GetInput = self.ic.CH341GetInput
                    self.ch341CloseDevice = self.ic.CH341CloseDevice
                    self.ch341WriteData = self.ic.CH341WriteData
                    self.ch341WriteRead = self.ic.CH341WriteRead
                    self.ch341SetOutput = self.ic.CH341SetOutput
                    self.ch341SetStream = self.ic.CH341SetStream

                elif os.name == 'posix':
                    self.ic = cdll.LoadLibrary(libPath)  # ch341接口

                    self.ch341GetInput = self.ic.CH34xGetInput
                    self.ch341CloseDevice = self.ic.CH34xCloseDevice
                    self.ch341WriteData = self.ic.CH34xWriteData
                    self.ch341WriteRead = self.ic.CH34xWriteRead
                    self.ch341SetOutput = self.ic.CH34xSetOutput
                    self.ch341SetStream = self.ic.CH34xSetStream

                print("ch341加载成功")
                return True
            except Exception as e:
                print("ch341加载失败")
                return False

    # 判断ch341是否插入
    # return：0未插入，1插入
    def open(self):
        if os.name == 'nt':  # Windows 环境
            try:
                self.fd = self.ic.CH341OpenDevice(0)
                if self.fd == -1:
                    print("CH341 device open failed on Windows.")
                    return False
                else:
                    self.fd = 0 #todo 改成扫描端口
                    print("CH341 device opened successfully on Windows.")
                    return True
            except Exception as e:
                print(f"Error occurred while opening CH341 device on Windows: {e}")
                return False

        elif os.name == 'posix':  # Linux 环境
            try:
                devices = glob.glob('/dev/ch34x_pis*')  # 动态查找设备
                if devices:
                    device_path = devices[0].encode()
                    self.fd = self.ic.CH34xOpenDevice(device_path)
                    if self.fd == -1:
                        print("CH341 device open failed on Linux.")
                        return False
                    else:
                        print("CH341 device opened successfully on Linux.")
                        return True
                else:
                    print("No CH341 device found on Linux.")
                    return False
            except Exception as e:
                print(f"Error occurred while opening CH341 device on Linux: {e}")
                return False

        else:
            print("Unsupported operating system.")
            return False
    
    def disconnect(self):
        close_device = getattr(self, "ch341CloseDevice", None)
        if self.fd not in (None, -1) and callable(close_device):
            close_device(self.fd)
        self.fd = None

    def connectCheck(self):
        if True == self.ch341GetInput(self.fd, ctypes.byref(self.deviceID)):
            return True
        else:
            return False
        
    # iic写数据
    # addr:iic从机地址
    # data：要写的数据列表
    # return：写入长度，不一定正确
    def write(self, addr, data):
        sLen = len(data)
        tmpData = []    # 临时列表
        tmpLen = sLen   # 发送数据

        pack = []   # 发送列表
        cnt = 20    # 每包数量
        packNum = sLen // cnt    # 拆包数量
        sLen %= cnt  # 不足字节数

        tmpData.extend(data)

        pack.append(self._mCH341A_CMD_I2C_STREAM)
        pack.append(self._mCH341A_CMD_I2C_STM_STA)
        pack.append(self._mCH341A_CMD_I2C_STM_OUT | 1)
        pack.append(addr << 1)
        for i in range(0, packNum):
            pack.append(self._mCH341A_CMD_I2C_STM_OUT | cnt)
            pack.extend(tmpData[0:20])
            del tmpData[0:20]
            pack.append(self._mCH341A_CMD_I2C_STM_END)
            sendBuf = (c_byte * len(pack))()
            for j in range(0, len(pack)):
                sendBuf[j] = pack[j]
            sendLen = (c_byte * 1)()
            sendLen[0] = len(pack)
            if not self.ch341WriteData(self.fd, sendBuf, sendLen):
                return 0
            if sendLen == 0:
                return 0
            pack.clear()
            pack.append(self._mCH341A_CMD_I2C_STREAM)
        if sLen >= 1:
            pack.append(self._mCH341A_CMD_I2C_STM_OUT | sLen)
            pack.extend(tmpData[0:sLen])
        pack.append(self._mCH341A_CMD_I2C_STM_STO)
        pack.append(self._mCH341A_CMD_I2C_STM_END)
        sendBuf = (c_byte * len(pack))()
        for j in range(0, len(pack)):
            sendBuf[j] = pack[j]
        sendLen = (c_byte * 1)()
        sendLen[0] = len(pack)
        if not self.ch341WriteData(self.fd, sendBuf, sendLen):
            return 0
        if sendLen == 0:
            return 0
        return tmpLen

    # iic读数据
    # addr：iic从机地址
    # data：读取数据列表。根据列表大小确定读取长度
    # return：读取长度，不一定正确
    def read(self, addr, data):
        if id(data) == 0 or len(data) == 0:
            return 0
        rLen = len(data)
        #print(f"rLen={rLen}")
        pack = []
        readBuf = []
        readLen = 0

        packNum = rLen // 30
        rLen %= 30
        if rLen == 0:
            rLen = 30
            packNum -= 1
        #print(f"packNum={packNum}")
        pack.append(self._mCH341A_CMD_I2C_STREAM)
        pack.append(self._mCH341A_CMD_I2C_STM_STA)
        pack.append(self._mCH341A_CMD_I2C_STM_OUT | 1)
        pack.append((addr << 1) | 0x01)
        pack.append(self._mCH341A_CMD_I2C_STM_MS | 1)
        for i in range(0, packNum):
            pack.append(self._mCH341A_CMD_I2C_STM_IN | 30)
            pack.append(self._mCH341A_CMD_I2C_STM_END)
            sendBuf = (c_byte * len(pack))()
            for j in range(0, len(pack)):
                sendBuf[j] = pack[j]
            recLen = (c_byte * 1)()
            recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)()
            if not self.ch341WriteRead(self.fd, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf):
                return 0
            if recLen == 0:
                return 0
            for j in range(0, recLen[0]):
                readBuf.append(recBuf[j])
            readLen += 30
            pack.clear()
            pack.append(self._mCH341A_CMD_I2C_STREAM)
        if rLen > 1:
            pack.append(self._mCH341A_CMD_I2C_STM_IN | (rLen - 1))
        pack.append(self._mCH341A_CMD_I2C_STM_IN | 0)
        pack.append(self._mCH341A_CMD_I2C_STM_STO)
        pack.append(self._mCH341A_CMD_I2C_STM_END)
        sendBuf = (c_byte * len(pack))()
        for j in range(0, len(pack)):
            sendBuf[j] = pack[j]
        recLen = (c_byte * 1)()
        recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)()
        if not self.ch341WriteRead(self.fd, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf):
            return 0
        if recLen[0] == 0:
            return 0
        for j in range(0, recLen[0]):
            readBuf.append(recBuf[j])
        data.clear()
        data.extend(readBuf)
        readLen = len(pack)
        #print(f"readLen={len(readBuf)}")
        return readLen

    # 设置int引脚状态
    # lvl：高低电平。1高电平，0低电平
    def set_int(self, lvl):
        status = (c_long * 1)()
        self.ic.CH341GetInput(0, status)
        time.sleep(0.01)
        if lvl:
            self.ch341SetOutput(self.fd, 0x03, 0xFF00, status[0] | self._mStateBitINT)
        else:
            self.ch341SetOutput(self.fd, 0x03, 0xFF00, status[0] & (~self._mStateBitINT))

    # 读取int引脚状态
    # 返回：高低电平
    def get_int(self):
        status = (c_long * 1)()
        self.ic.CH341GetInput(0, status)
        return (status[0] & self._mStateBitINT) >> 10

    # 设置IIC速度
    # return：0错误，1成功
    def set_speed(self, speed):
        if speed != self.IIC_SPEED_20 and speed != self.IIC_SPEED_100 and speed != self.IIC_SPEED_400 and speed != self.IIC_SPEED_750:
            return False
        if False == self.ch341SetStream(self.fd, speed | 0):
            print("speed err")
            return False
        else:
            return True
