# -*- coding: utf-8 -*-
# Copyright (c) 2025 SDRLDQ
# All rights reserved
import machine
from machine import UART
import time
import _thread
import struct

STR_MAX_LEN = 28

class DWIN:
    """DWIN 串口屏幕控制类"""
    
    def __init__(self, uart_id=1, tx_pin=10, rx_pin=9, baudrate=115200, data_bits=8, stop_bits=1, parity=None):
        
        """
        UART1->RS232初始化DWIN显示屏        
        参数:
            uart_id: UART端口号 (ESP32的UART1)
            tx_pin: 发送引脚 (ESP32 UART1默认TX=10)
            rx_pin: 接收引脚 (ESP32 UART1默认RX=9)
            baudrate: 波特率 115200
            data_bits:8
            stop_bits:1
            parity:None
        """
        #self.uart = UART(uart_id, baudrate)
        self.uart = UART(uart_id, baudrate=baudrate, bits=data_bits, parity=parity,stop=stop_bits)
        self.uart.init(baudrate=baudrate, tx=tx_pin, rx=rx_pin)
        self._lock = _thread.allocate_lock()
        self.rx_buffer = bytearray(40)
        
        self._handle_stop_test  = None
        self._handle_start_test = None
        
        self._handle_capture_background = None    
        self._handle_capture_sample 	= None
        self._write_eeprom				= None
        
        self._update_cfg_callback = None

    def _send_bytes(self, data):
        """发送字节数据到显示屏"""
        with self._lock:
            self.uart.write(bytes(data))
            time.sleep_ms(2)  # 等待数据发送完成
    
    def put_string(self, data, length):
        """发送字符串数据到显示屏"""
        if isinstance(data, (bytes, bytearray)):
            self._send_bytes(data[:length])
        else:
            self._send_bytes(bytes(data[:length]))
    
    def jump_page(self, page_id):
        """
        切换到指定页面
        
        参数:
            page_id: 页面ID
        """
        data = bytearray([0x5A, 0xA5, 0x04, 0x80, 0x03, page_id >> 8, page_id & 0xFF])
        self.put_string(data, len(data))
    
    def read_reg(self, reg_addr):
        """
        读取寄存器值 寄存器地址:0x20 长度:07  read RTC 
        
        参数:
            reg_addr: 寄存器地址
        """
        data = bytearray([0x5A, 0xA5, 0x03, 0x81, reg_addr, 0x07])
        self.put_string(data, len(data))
    
    def write_reg(self, reg_addr, value):
        """
        写入寄存器值
        
        参数:
            reg_addr: 寄存器地址
            value: 要写入的值
        """
        data = bytearray([0x5A, 0xA5, 0x04, 0x80, reg_addr, value >> 8, value & 0xFF])
        self.put_string(data, len(data))
    
    def write_var(self, addr, value):
        """
        写入变量值到LCD显示
        
        参数:
            addr: 变量地址
            value: 要写入的值（16位整数）
        """ 
        value = int(value)
        data = bytearray([0x5A, 0xA5, 0x05, 0x82, 
                         addr >> 8, addr & 0xFF,
                         value >> 8, value & 0xFF])
        self.put_string(data, len(data))
        
    def print_bytearray(self,data):
        """foreach print hex value """  
        if not isinstance(data, (bytes, bytearray)):
            raise TypeError("输入必须是字节序列 (bytes 或 bytearray)")
        for i, byte in enumerate(data):
            # 格式化为两位数大写十六进制，带0x前缀
            print(f"bytes [{i:03d}]: 0x{byte:02X} ({byte:3d})")
        
    def write_const_string(self, String_ASCII,addr=0x160):
        #unsigned char const TEST_CMD[6]={0x5a,0xa5,21,0x82,0x01,0x60};
        
        #data = bytearray([0x5a,0xa5,3 + STR_MAX_LEN,0x82,0x01,0x60])
        data = bytearray([0x5a,0xa5,3 + STR_MAX_LEN,0x82,(addr)>>8,addr&0xFF])
        #增加判断字符串是否非中文
        string = bytearray(String_ASCII.encode("ascii"))#  bytes对象转换为bytearray
        #string = bytearray([0xB4,0xF3,0xC9,0xB5,0xB1,0xC6,0x0d,0x0a])  #大傻逼
        _len = len(string) #字符串长度
        #self.print_bytearray(string) 
        #print(f"write_const_string len = {_len} string = {string} \r")
        if(_len<STR_MAX_LEN):
            appstrLen = STR_MAX_LEN-_len;
            tailstr = bytearray(b' ' *appstrLen) # 创建空格填充字节数组
            string.extend(tailstr )             # 填充空格到末尾          
        elif (_len>STR_MAX_LEN):
            string = string[:STR_MAX_LEN]  # 截断超长部分     
        else:       
            pass
        
        #print(f"string = {string}\r")
        data.extend(string) #将处理后的字符串追加到协议头后面
        datalen = 6 + STR_MAX_LEN #len(data)
        #print(f"data={data} len = {datalen}") 
        
        self.put_string(data, datalen)
        pass
    
    
    def read_var(self, addr,wordCnt):
        """
        读入32位变量值从LCD显示 
        参数:
            addr: 变量地址
            wordCnt : 数据宽度word
        返回
            要写入的值（32位整数）
        """
        value = 0
        if not (0<wordCnt<3):
            raise OSError(f'wordCnt {wordCnt}   error!') 
        data = bytearray([0x5A, 0xA5, 0x04, 0x83,
                         addr >> 8, addr & 0xFF,wordCnt])
        self.put_string(data, len(data)) 
        time.sleep_ms(1 + len(data))  
        bReceived = self.receive_data()
        if bReceived ==True :
            #处理数据 
            respone_addr = (self.rx_buffer[4] << 8) + self.rx_buffer[5]
            if(respone_addr!=addr):
                raise OSError(f'respone_addr {respone_addr} != {addr} error!')
            else:
                if(wordCnt==1):
                    value = self.rx_buffer[7]<<8+self.rx_buffer[8]
                elif (wordCnt==2):
                    value = self.rx_buffer[7]<<24
                    value += self.rx_buffer[8]<<16
                    value += self.rx_buffer[9]<<8
                    value += self.rx_buffer[10]
                print(f"addr {addr}: value:{value}")
        else: 
            raise OSError('no data received from dwin monitor')
        return value
        
    
    def write_var_32(self, addr, value):
        """
        写入32位变量值到LCD显示
        
        参数:
            addr: 变量地址
            value: 要写入的值（32位整数）
        """
        value = int(value)
        data = bytearray([0x5A, 0xA5, 0x07, 0x82,
                         addr >> 8, addr & 0xFF,
                         (value >> 24) & 0xFF,
                         (value >> 16) & 0xFF,
                         (value >> 8) & 0xFF,
                         value & 0xFF])
        self.put_string(data, len(data))
    
    def init_lcd(self, eeprom_data=None):
        """
        初始化LCD显示
        
        参数:
            eeprom_data: EEPROM数据读取函数，如果不提供则跳过EEPROM初始化
        """
        if eeprom_data is None:
            return
            
        data = bytearray([0x5A, 0xA5, 0x05, 0x82, 0x00, 0x00, 0x00, 0x00])
        
        for addr in range(0xFF):#0~0xff数据块上传到LCD显示屏
            data[5] = addr
            data[6] = eeprom_data(addr * 2)
            data[7] = eeprom_data(addr * 2 + 1)
            self.put_string(data, 8)
            
    def proccess_Get_Reg_value(self,addr):
        var_width = self.rx_buffer[6]*2
        var_Value = 0;
        for i in range(var_width):
            var_Value<<=8
            var_Value |= self.rx_buffer[i+7] 
            #print(f"var_Value = {var_Value}")
            var_Value = (var_Value & 0xFFFF) - (0x10000 if var_Value & 0x8000 else 0)
            #print(f"var_Value = {var_Value}")
        if(self._write_eeprom!=None):
            self._write_eeprom(var_width,addr*var_width,var_Value)
        if(self._update_cfg_callback!=None):
            self._update_cfg_callback()   #此处地址不得*2
            print("_update_cfg_callback actived!")
        print(("process register Value var_width {:02X} addr:{:02X} = {:02X}!\r\n").format(var_width,addr,var_Value))
        pass
    
    def proccess_Key_cmd(self):
        cmd_addr = (self.rx_buffer[4] << 8) + self.rx_buffer[5]
        if cmd_addr == 0x00DF:  # 按键消息
            key_data = self.rx_buffer[8]
            self._handle_key_press(key_data) 
            print(("process Key command {:02X} successed!\r\n").format(key_data))
        else:
            # 保存参数
            self.proccess_Get_Reg_value(cmd_addr)
            pass
        
    def proccess_decode_cmd(self):   
        operator = {
            0x83: self.proccess_Key_cmd,    # 处理按键消息
            0x81: self._handle_system_date_message,# 处理系统消息
            }
        # 检查帧头
        if self.rx_buffer[0] == 0x5A and self.rx_buffer[1] == 0xA5:
            key = self.rx_buffer[3]
            if key in operator:
                operator[key]()
            else:
                print('Unknown Command')
        else:
            pass
    
    def receive_data(self):
        bPackStart = False
        bPackEnd = False
        iTimeout = 10
        # 清空rx_buffer缓冲区 
        self.rx_buffer = []
        while bPackEnd == False:
            """处理接收到的数据"""
            iTimeout-=1
            if self.uart.any():
                # 读取接收到的数据
                time.sleep_ms(1)
                received = self.uart.read()
                if received:
                    bPackStart = True
                    # 保存接收到的数据开始拼接到rx_buffer缓冲区 
                    if len(received) > 0:
                        self.rx_buffer += received 
            else:
                if bPackStart== True:
                    bPackEnd = True 
                    print('Input Key value: ' + ' '.join('{:02X}'.format(x) for x in self.rx_buffer))
                    break
            if iTimeout==0:
                #print('receive timeout!\r')
                break
        return bPackEnd
        
        
    def process_received_data(self):
        bReceived = self.receive_data()
        if(bReceived == True ):
            self.proccess_decode_cmd()
            
                
    def _Reset(self): 

        machine.reset()

    
    def _handle_key_press(self, key_data):
        """
        处理按键消息
        
        参数:
            key_data: 按键值
        """
        operator = {
            0x01: self._handle_start_test,# 开始测试
            0x02: self._handle_stop_test,# 停止测试
            0x12: self._handle_capture_background,# 采集背景            
            0x13: self._handle_capture_sample,#    采集样品
            0xCC: self._Reset,    # 系统复位
            }
        if key_data in operator: 
            operator[key_data]()
            print('dwin-->_handle_key_press key_data ={:02X}'.format(key_data) )
        else: 
            pass
 
    
    def _handle_system_date_message(self):
        """处理系统消息"""
        # 在这里添加命令0x81返回的数据


        
        print(("dwin-->_handle_system_date_message !\r\n"))
        pass
    
    def reg_callback(self, reg_addr, value):
        """
        注册回调函数，当寄存器发生变化时会被调用
        
        参数:
            reg_addr: 寄存器地址
            value: 新值
        """ 
        dic_callback = {
            '_handle_start_test': self._handle_start_test,
            '_handle_stop_test': self._handle_stop_test
        }
        if(reg_addr == '_handle_start_test'):
            self._handle_start_test = value
        if(reg_addr == '_handle_stop_test'):
            self._handle_stop_test = value 
        if(reg_addr == '_write_eeprom'):
            self._write_eeprom = value 
        if(reg_addr == '_update_cfg_callback'):
            self._update_cfg_callback = value
            
        print(f"Reg changed: {reg_addr}, new value: {value}")
        

    def run(self):
        """运行主循环"""
        self.init_lcd()
        # 切换到页面 0
        #self.jump_page(0)  # 跳转到初始页面
        
        while True:
            self.process_received_data()
            

# 使用示例
def example():
    # 创建DWIN实例
    dwin = DWIN(uart_id=1)
    
    # 写入一些测试数据
    #str = bytearray(b"大傻逼!".encode('ascii')) 
    #str = bytearray("foolish pig!".encode('ascii'))
    str = "foolish pig foolish pig!" 
    dwin.write_const_string(str)
    dwin.write_var(0x110, 250)  		# 写入16位数据
    dwin.write_var(0x111, 251)  		# 写入16位数据
    dwin.write_var(0x112, 0)  		# 写入16位数据
    dwin.write_var_32(0x0113, 255255)  	# 写入32位数据
     
    
    # 运行主循环
    dwin.run()

if __name__ == '__main__': 
    example()
    
    
       # End File