try:
    import usocket as _socket
except ImportError:
    import socket as _socket


from wiznet5k import WIZNET5K
from machine import Pin, SPI,UART
import wiznet5k_socket as socket
import time
import ujson 
import os
import machine
import network
import power
import ch9329
import _thread
import random

g_nTimeOut = 0.001;#超时时间
g_CControlCocket = 0; #全局socket
g_Mac5 = (0xde, 0xad, 0xbe, 0xef, 0xfe);#全局MAC地址前五位


uart = UART(2, 9600)
#打开配置文件
with open("config.json") as fp:
    config = ujson.load(fp)
with open("update.json") as fp:
    updateconfig = ujson.load(fp)
#中控地址  
dest_addr = (updateconfig['CenterControl']['IP'], updateconfig['CenterControl']['port']  )
g_Mac = g_Mac5 + (config['w5500']['macEnd'],);#从配置文件中获取mac地址的最后一位  添加的时候由于是元组 所以是按照(,) 括号加逗号的形式，python的特殊语法问题。

SNSR_SOCK_CLOSED = const(0x00)
SNSR_SOCK_CLOSE_WAIT = const(0x1C)
SNSR_SOCK_ESTABLISHED = const(0x17)




def LanW5500Init(mac):
    spi = SPI(2)
    cs = Pin(5,Pin.OUT,)
    rst=Pin(34)
    nic = WIZNET5K(spi,cs,rst,True,mac)   
    
    print("\n\n以太网芯片版本:", nic.chip)
    print("网卡MAC地址:", [hex(i) for i in nic.mac_address])
    print("IP地址:", nic.pretty_ip(nic.ip_address))
    return
 
def _resolve_addr(addr):
    if isinstance(addr, (bytes, bytearray)):
        return addr

    family = _socket.AF_INET

    if len(addr) != 2:
        family = _socket.AF_INET6

    if not addr[0]:
        host = "127.0.0.1" if family == _socket.AF_INET else "::1"
    else:
        host = addr[0]

    return _socket.getaddrinfo(host, addr[1]) 

        
 
#发送ftp命令
#ftp为创建的套接字
#command为要发生的命令
def FtpSnedCommand(ftp,command):
    encoding = "latin-1"  
    line = command + '\r\n'
    resp = ftp.send(line.encode(encoding))
    return
#登录ftp服务器
#返回套接字
def FTPLogin():
    ftp = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建套接字
    port = config['ftp']['port']
    addr = config['ftp']['host']
    #print(addr)
    #print(port) 
    ftp.connect((addr,port))#连接
    user = config['ftp']['user']
    command = 'USER ' + user
    FtpSnedCommand(ftp,command)
    print(ftp.recv())
    passwd = config['ftp']['pass']
    command = ('PASS ' + passwd)
    FtpSnedCommand(ftp,command)
    print(ftp.recv())
    return ftp
#退出FTP服务器
def FTPQuit(ftp):
    ftp.close()
    return
#进入ftp指定目录
#ftp为创建的套接字
#dirname为指定的ftp服务器上目录
def FTPcwd(ftp,dirname):
    cmd = 'CWD ' + dirname
    FtpSnedCommand(ftp,cmd)
    print(ftp.recv())
    return

def _find_parentheses(s):
    s = str(s);
    left = s.find("(",0)
    if left < 0:
        raise ValueError("missing left delimiter")

    right = s.find(')', left + 1)
    if right < 0:
        # string should contain '(...)'
        raise ValueError("missing right delimiter")

    return left, right

def parse227(resp):
    """Parse the '227' response for a PASV request.

    Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'

    Return ('host.addr.as.numbers', port#) tuple.
    """
    resp = str(resp)
   

    left, right = _find_parentheses(resp)
    numbers = tuple(int(i) for i in resp[left+1:right].split(',', 6))
    host = '%i.%i.%i.%i' % numbers[:4]
    port = (numbers[4] << 8) + numbers[5]
    
    return host, port;
#从FTP下载指定文件  并且直接下载到本地电脑的当前文件夹 并且文件名称也相同
#资料查询https://codeantenna.com/a/E1q7JDmlDp
#单个文件不要超过20k，否则w5500的驱动在recv的时候申请不到内存
def FTPDownFile(ftp,FileName,blocksize=1024):
    FtpSnedCommand(ftp,'TYPE I')#二进制模式
    print(ftp.recv())
    FtpSnedCommand(ftp,'PASV')#被动模式
    data = ftp.recv();
    print(data)
    host, port = parse227(data)
    print(host,port)
    DataSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建套接字    
    DataSocket.connect((host,port))#连接  根据被动模式返回的端口和ip连接ftp服务器

    cmd = 'RETR ' + FileName #发送请求下载文件
    FtpSnedCommand(ftp,cmd)
    #print(ftp.recv())    
    
    fd = open(FileName,'w+')#创建新文件 并且能读写
    print(cmd)
    while True:
        #print(1)
        #print(ftp.recv()) 
        data = DataSocket.recv()#根据blocksize来接收数据  通过Data Socket来读取要下载文件的数据
        #print(2)
        if not data:
            break#没有数据就退出循环
        #print(data)
        fd.write(data)#写入收到的数据
    DataSocket.close()#关闭文件下载的socket
    fd.close()  #关闭文件句柄
    #print("文件下载结束")
    return
#初始化网卡驱动 并且从ftp上更新固件
def InitNetworkDriveAndUpdateFirmware():
    global g_Mac;
    LanW5500Init(g_Mac) #初始化w5500
    ftp = FTPLogin();#登录ftp服务器 账号密码由config.json文件控制
    FTPcwd(ftp,config['update']['ftpfolder']);#切换到ftp上的更新文件夹 由config.json文件控制
    listFileName = config['update']['filename'];
    FTPDownFile(ftp,listFileName)#更新 update.json文件
    count = 0;#初始化文件的数量和下标
    #打开update.json配置文件
    global fp
    fp.close();#关闭 config.json 配置文件
    #print(listFileName)
    with open(listFileName) as fp2:
        update = ujson.load(fp2)   #打开同步文档
    FileCount =update['update']['filecount']
    print("同步文件数量："+str(FileCount))   
    while (1):#根据update字段的filecount来控制同步文件的数量
        count = count + 1
        if(FileCount < count):#文件同步数量
            break
        FTPDownFile(ftp,update['update']['file'+str(count)])#根据update.json文件来同步固件
        print("更新文件："+update['update']['file'+str(count)]+" 序号："+str(count))
        time.sleep(5)
    fp2.close();#关闭update.json文件
    FTPQuit(ftp);#退出ftp服务器
    print("同步完成")   
    return
#创建新套接字用于中控通讯
def CenterControlCreate(nTimeout):
    #CControl = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)#创建UDP连接
    CControl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#创建TCP连接
    CControl.settimeout(nTimeout);#设置超时
    
    return CControl
#连接中控
def CenterControlCount(CControlCocket):
    send_data = "connect-" + config['SerialNumber']['username'] + "-" +  config['SerialNumber']['Number']
    #CControlCocket.connect(dest_addr)
    #print("当前状态2：%d" % CControlCocket.status)
    #CControlCocket.send(send_data.encode('utf-8'))
    try:
        CControlCocket.sendto(send_data.encode('utf-8'), dest_addr)
    except:
        #print("连接发生异常")
        time.sleep(CControlCocket._timeout)
    else:
        print("连接中控成功")
    return
def CenterControlRecv(CControlCocket):
    while True:
        #if CControlCocket.status != SNSR_SOCK_ESTABLISHED:
            #print("当前状态：%d" % CControlCocket.status)
        data = CControlCocket.recv()
        if data:          
            print(str(data, 'utf8'), end='')
            CenterControlRecvHandle(data)
        else:#如果收包超时            
            if CControlCocket.status == SNSR_SOCK_CLOSED or CControlCocket.status == SNSR_SOCK_CLOSE_WAIT:#连接断开
                #print("主动重新连接") 
                CenterControlCount(CControlCocket)                              
            else:
                time.sleep(CControlCocket._timeout)
                print("收包超时：%d" % CControlCocket.status)
            	#break 
    return
#随机等待微妙
def RandSleep(nMin,mMax):
    nSleepTiem = random.randint(nMin,mMax);
    time.sleep(nSleepTiem/1000);
    return
#处理收到的数据包  
def CenterControlRecvHandle(recvdata):
    #数据包大于500 表示粘包太严重 递归会崩 所以直接返回
    if(len(recvdata)>500):
        return
    dataSize = 2;#包长度
    #print("----------------------------")
    #for index in range(0,len(recvdata)):
        #print(hex(recvdata[index]))
    if (recvdata[0] == 0x57 and recvdata[3] == 0x6):#直接发送的字符串数据给USB  这里面还包含了直接的命令 主要用于在windows环境下的解析        
        dataSize = int(recvdata[4]) + 4 + 1 + 1;#下标为4表示数据的长度 包的长度 要加头4个字节和 1个字节的校验和 + 1个字节长度位
        ch9329.SendHIDStringData(recvdata,dataSize);
        print("收到转发给USB的string包");
    elif (recvdata[0] == 0x57 and recvdata[3] == 0x5):#发送相对鼠标命令        
        dataSize = 11 #鼠标包的固定长度
        ch9329.SendHIDStringData(recvdata,dataSize);
    elif (recvdata[0] == 0x57 and recvdata[3] == 0x2):#发送键盘命令   会自动弹起按键
        #print("----------------------------")
        #for index in range(0,len(recvdata)):
            #print(hex(recvdata[index]))
        dataSize = 14 #键盘包的固定长度
        #由于发送过过来的键盘命令只有按下指令，为了减少发包数据这里就需要手动添加键盘弹起命令
        ch9329.SendHIDStringData(recvdata,dataSize);
        RandSleep(1,50)#随机延迟100-300微妙
        RetBytes = bytearray()#创建一个空的字节数组
        for index in range(5):#直接复制文件头
            RetBytes.append(recvdata[index])
        for index in range(8):#设置键盘弹起数据
            RetBytes.append(0x0)  
        ch9329.AddBytesSum(RetBytes)#校验和      
        ch9329.SendHIDStringData(RetBytes,dataSize);
    elif (recvdata[0] == 0x57 and recvdata[3] == 0x12):#发送键盘命令  不自己弹起按键 保持按住状态
        dataSize = 14 #键盘包的固定长度
        #recvdata[3] = 0x2;#0x12为自定义的按键码 所以这里要改回来  直接复制报错
        RetBytes = bytearray()#创建一个空的字节数组
        for index in range(0,len(recvdata)):
            if (index == 3):
                RetBytes.append(0x2) #0x12为自定义的按键码 所以这里要改回来
            elif (index == 13):
                RetBytes.append(recvdata[index]-0x10)#重新计算校验和
            else:
                RetBytes.append(recvdata[index])
            #print(hex(recvdata[index]))    
        ch9329.SendHIDStringData(RetBytes,dataSize);
    elif (recvdata[0] == 0x57 and recvdata[3] == 0x4):#发送绝对鼠标指令
        dataSize = 13 #鼠标包的固定长度
        ch9329.SendHIDStringData(recvdata,dataSize);
    elif (recvdata[0] == 0xCE):#发送键盘命令  新版本的 和lua端统一 
        RecvUsbData = ch9329.xorRecvData(recvdata)#异或解密
        ch9329.hanleKeyboardEsp32(RecvUsbData)#处理鼠标数据
    elif str(recvdata, 'utf8') == '01':
        print("收到开机命令")
        power.poweron();
        print("执行开机命令")
    elif str(recvdata, 'utf8') == '02':
        print("收到关机命令")
        power.power0ff();
        print("执行关机命令")
    elif str(recvdata, 'utf8') == '03':
        print("收到重启命令")
        power.reboot();
        print("执行重启命令")
    elif str(recvdata, 'utf8') == '00':
        print("收到心跳包")
    
    if(len(recvdata)>dataSize):#包的长度大于解析的长度，说明这个包粘包了
        #print("size:" + str(len(recvdata)))
        newData = bytearray()#创建一个空的字节数组
        for index in range(dataSize,len(recvdata)):#将后续没解析的包 提取出来
            newData.append(recvdata[index])
        CenterControlRecvHandle(newData)#递归解析后续没有解析完成的数据包
    return
#中控通讯和网络配置文件同步流程
def CenterControl():
     #初始化网卡驱动，并且同步配置文件
    InitNetworkDriveAndUpdateFirmware();
    time.sleep(5);
    nTimeOut = 2;#超时时间
    CControlCocket = CenterControlCreate(nTimeOut);
    CenterControlCount(CControlCocket);#连接中控服务端
    CenterControlRecv(CControlCocket);#循环收包，并且断线重连
    return
#给中控发送消息
def sendtoControlNotCocket(SendData):
    global g_CControlCocket
    if (SendData[0] == 0xCD): #表示这个数据需要转发给中控        
        if (g_CControlCocket.status == SNSR_SOCK_ESTABLISHED): #确认当前已经和中控连接才发送消息
            g_CControlCocket.send(SendData)#发送给中控
    return
#给中控发送消息
def sendtoControl(CControlCocket,SendData):
    if (SendData[0] == 0xCD): #表示这个数据需要转发给中控        
        if (CControlCocket.status == SNSR_SOCK_ESTABLISHED): #确认当前已经和中控连接才发送消息
            CControlCocket.send(SendData)#发送给中控
    return
#中控处理
def mainControl2():
    #初始化网卡驱动，并且同步配置文件
    global g_CControlCocket
    print("begin...")
    InitNetworkDriveAndUpdateFirmware();
    #time.sleep(5);
    RandSleep(1000,10000)
    g_CControlCocket = CenterControlCreate(g_nTimeOut); #全局socket
    
    CenterControlCount(g_CControlCocket);#连接中控服务端
    
    print("中控线程开始工作！")
    while True:
        #收取中控的网络数据包
        data = g_CControlCocket.recv()
        if data:          
            #print(str(data, 'utf8'), end='')
            CenterControlRecvHandle(data)#处理收到的数据包
        else:#如果收包超时            
            if g_CControlCocket.status == SNSR_SOCK_CLOSED or g_CControlCocket.status == SNSR_SOCK_CLOSE_WAIT:#连接断开
                #print("主动重新连接") 
                CenterControlCount(g_CControlCocket)                              
            else:
                time.sleep(g_CControlCocket._timeout)
    return
#将USB转发和中控处理统一到一起
def mainControl():
    #初始化网卡驱动，并且同步配置文件
    print("begin...")
    InitNetworkDriveAndUpdateFirmware();
    time.sleep(5);
    nTimeOut = 0.001;#超时时间
    CControlCocket = CenterControlCreate(nTimeOut);
    CenterControlCount(CControlCocket);#连接中控服务端
    while True:
        #收取中控的网络数据包
        data = CControlCocket.recv()
        if data:          
            #print(str(data, 'utf8'), end='')
            CenterControlRecvHandle(data)#处理收到的数据包
        else:#如果收包超时            
            if CControlCocket.status == SNSR_SOCK_CLOSED or CControlCocket.status == SNSR_SOCK_CLOSE_WAIT:#连接断开
                #print("主动重新连接") 
                CenterControlCount(CControlCocket)                              
            else:
                time.sleep(CControlCocket._timeout)
                #print("收包超时：%d" % CControlCocket.status)
            	#break
        #收取USB的数据
        #print("11")
        RecvData = uart.read()
        #print("22")
        if (type(RecvData) == bytes) :
            #print("head:"+hex(RecvData[0]))
            if (RecvData[0] == 0x57 ): #判断固定的数据头
                if(RecvData[1] == 0xAB):#判断固定的数据头
                    if(RecvData[3] == 0x87):#CMD_READ_MY_HID_DATA  读取从USB发送来的自定义的数据
                    #从usb发送数据11收到的数据如下
                    #57 ab 00 87 01 11 9b
                        #print(RecvData)
                        #print(GetBytesSum(RecvData))
                        usbdata = bytes(ch9329.GetUsbData(RecvData));#获取USB发送的数据
                        sendtoControl(CControlCocket,usbdata);
                        #print("usbdata:")
                        #for index in range(len(usbdata)):
                            #print(hex(usbdata[index]))
                        ch9329.transmit(usbdata)#判断是否转发
    return
def main():
    mainControl2();
    return
if __name__ == "__main__":
    main()


