zoukankan      html  css  js  c++  java
  • 重构+优化 micropython 下 k210 (esp8285) 的 AT network 通信过程(附代码,顺便讲讲状态机实现)。

    回头再解释,先 mark 2020年4月28日。

    2020年5月1日 今日有空,稍微更新一下,主要在这里备份一下相关的 code 和详细的问题分析和具体解释。

    继着上次的 https://www.cnblogs.com/juwan/p/12389600.html 的内容,上次只定位了问题和简单解决了问题,调整了 Python 的 Code ,现在主要是旧 Code 不稳定,下载的文件容易出问题,容易因为二进制文件产生错误。

    那么如何解决呢?

    我回顾一下我的这 4 天的开发历程,第一天,基于旧接收框架重新整理 Code ,第二天准备到位开始测试,测试崩盘没有任何改造,镇定思痛,第三天重新上状态机架构,第四天大量测试,缝缝补补后符合预期执行,趋于完美。

    先上逻辑分析仪捕获数据观察数据流,注意我压缩了很多写的过程,只交待重点,有理有据的代码让人信服,如果有漏洞也是细节的马虎,思路对了大体是不会有太大问题的。

    在 HTTP 下载文件的时候,可以看到它接收数据是以 +IPD 开头的数据,如 '+IPD,3,1452:' or '+IPD,1452:' 两类,经过测试,确实是符合预期的返回指定长度的数据,但会在最后的结束的时候附带 CLOSED 数据表示传输结束。

    这里我们注意到两个问题,以及旧代码为什么不能抵抗高速传输的异常数据所产生的错误,这在上次的分析就已经提到这个数据流的问题,我们先理清问题。

    我们知道了传输的规则和传输的形式,例如 AT 指令 和 传输的数据 是混在同一条通道中的,这意味着二进制数据中一旦出现类似 AT 指令的数据必然导致后续的数据错乱,又或者是传输的缓冲区异常导致程序跑飞等等。

    这些错误都在旧代码中体现了,这里不再细讲,直接开始编写新代码适用新的接口。

    我们知道这个函数是可重入的,所以一开始在剥离基础框架的时候就要最大化的保留重入的接口,例如,接收数据完成后,将数据保留到一个缓冲区中,在下一次的重入的时候,取走足够数量的数据,如果不够,则继续接收数据,如下代码的设计。

        // required data already in buf, just return data
        size = Buffer_Size(&nic->buffer);
        if (size >= out_buff_len) { // 请求的代码长度小于缓冲区的长度,直接返回数据
            Buffer_Gets(&nic->buffer, (uint8_t *)out_buff, out_buff_len);
            if (data_len) {
                *data_len = out_buff_len;
            }
            size = size > out_buff_len ? out_buff_len : size;
            frame_sum -= size; // 总体的缓冲区数据要对应减少,同时判断是否没有更多的数据了,如果为负数则报错。
            // printk("Buffer_Size frame_sum %d size %d *data_len %d
    ", frame_sum, size, *data_len);
            if (frame_sum <= 0) {
                Buffer_Clear(&nic->buffer);
                if (*peer_closed) { //buffer empty, return EOF
                    return -2;
                }
            }
            return out_buff_len;
        }
    

    围绕这个思路去,这个函数就形成一种非常适合 FSM 状态机的接口,同时必须要有 buffer 去保留上一次的状态与数据。

    为此可以将其构建一种基础状态机模型,二话不说先上架构,保持 IDLE, IPD, DATA, EXIT 四种状态,本来有 CLOSED 状态的,后来想了一下还不到时候就移除了。

    
        enum fsm_state { IDLE, IPD, DATA, EXIT } State = IDLE;
        static uint8_t tmp_buf[1560 * 2] = {0}; // tmp_buf as queue 1560
        uint32_t tmp_bak = 0, tmp_state = 0, tmp_len = 0, tmp_pos = 0;
        mp_uint_t interrupt = 0, uart_recv_len = 0, res = 0;
        while (State != EXIT) {
            // wait any uart data
            uart_recv_len = uart_rx_any(nic->uart_obj);
            if (uart_recv_len > 0) {
    
                if (tmp_len >= sizeof(tmp_buf)) { // lock across
                    // printk("lock across frame_len %d tmp_len %d
    ", frame_len, tmp_len);
                    tmp_len = 0;
                }
    
                res = uart_stream->read(nic->uart_obj, tmp_buf + tmp_len, 1, &err);
                // printk("%s | tmp_buf %s res %d err %d
    ", __func__, tmp_buf, res, err);
                if (res == 1 && err == 0) {
                    
                    interrupt = mp_hal_ticks_ms();
                    // backup tmp_len to tmp_pos (tmp_pos - 1)
    
                    tmp_pos = tmp_len, tmp_len += 1; // buffer push
                    // printk("[%02X]", tmp_buf[tmp_pos]);
    
                    if (State == IDLE) {
    
                        continue;
                    }
    
                    if (State == IPD) {
    
                        continue;
                    }
    
                    if (State == DATA) {
                        // printk("%s | frame_len %d tmp_len %d tmp_buf[tmp_pos] %02X
    ", __func__, frame_len, tmp_len, tmp_buf[tmp_pos]);
                        
                        continue;
                    }
    
                } else {
                    State = EXIT;
                }
            }
            
            if (mp_hal_ticks_ms() - interrupt > timeout) {
                break; // uart no return data to timeout break
            }
    
            if (*peer_closed) {
                break; // disconnection
            }
        }
    
    

    我们看到这个 FSM 的基础保证是,确保访问边界被控制住,防止程序指针异常访问导致跑飞(在我前一篇有所提及)。

                if (tmp_len >= sizeof(tmp_buf)) { // lock across
                    // printk("lock across frame_len %d tmp_len %d
    ", frame_len, tmp_len);
                    tmp_len = 0;
                }
    

    其次是超时机制,确保任何情况下都可以离开状态机。

            if (mp_hal_ticks_ms() - interrupt > timeout) {
                break; // uart no return data to timeout break
            }
    

    这样可以保障程序在任何场合下都不会陷入底层无法离开。

    接下来就是当缓冲区有数据、数据接收正常、以及每次读取一字节成功的基础上,进行字符串的匹配和状态转移的分类处理。

            // wait any uart data
            uart_recv_len = uart_rx_any(nic->uart_obj);
            if (uart_recv_len > 0) {
    
                if (tmp_len >= sizeof(tmp_buf)) { // lock across
                    // printk("lock across frame_len %d tmp_len %d
    ", frame_len, tmp_len);
                    tmp_len = 0;
                }
    
                res = uart_stream->read(nic->uart_obj, tmp_buf + tmp_len, 1, &err);
                // printk("%s | tmp_buf %s res %d err %d
    ", __func__, tmp_buf, res, err);
                if (res == 1 && err == 0) {
                    
                    interrupt = mp_hal_ticks_ms();
                    // backup tmp_len to tmp_pos (tmp_pos - 1)
    
                    tmp_pos = tmp_len, tmp_len += 1; // buffer push
                    // printk("[%02X]", tmp_buf[tmp_pos]);
    
                    if (State == IDLE) {
    
                        continue;
                    }
    
                    if (State == IPD) {
    
                        continue;
                    }
    
                    if (State == DATA) {
                        // printk("%s | frame_len %d tmp_len %d tmp_buf[tmp_pos] %02X
    ", __func__, frame_len, tmp_len, tmp_buf[tmp_pos]);
                        
                        continue;
                    }
    
                } else {
                    State = EXIT;
                }
            }
    

    这时候最后一个状态是用来离开的标记。

    我们开始脑里模拟执行过程和假设,第一种状态就是空转(IDLE),基本上每个状态机都是从这里开始的。

    在 IDLE 期间要做的事情就是清理无关的数据,如下代码,直到符合预期的怀疑对象出现为止。

                    if (State == IDLE) {
                        if (tmp_buf[tmp_pos] == '+') {
                            tmp_state = 1, tmp_bak = tmp_pos, State = IPD;
                            continue;
                        } else {
                            // printk("(%02X)", tmp_buf[tmp_pos]);
                            tmp_len -= 1; // clear don't need data, such as (0D)(0A)
                            continue;
                        }
                    }
    

    如上代码,在空转状态下,遇到 '+' 出现后,会进行假设性的状态转移,如果不符合预期的数据会跌回 ILDE 状态,然后我们看看 IPD 状态的实现。

    
                        if (tmp_pos - tmp_bak > 12) { // Over the length of the '+IPD,3,1452:' or '+IPD,1452:'
                            tmp_state = 0, State = IDLE;
                            continue;
                        }
    
                        if (0 < tmp_state && tmp_state < 5) {
                            // printk("(%d, %02X) [%d, %02X]
    ", tmp_pos, tmp_buf[tmp_pos], tmp_pos - tmp_bak, ("+IPD,")[tmp_pos - tmp_bak]);
                            if (tmp_buf[tmp_pos] == ("+IPD,")[tmp_pos - tmp_bak]) {
                                tmp_state += 1; // tmp_state 1 + "IPD," to tmp_state 5
                            } else {
                                tmp_state = 0, State = IDLE;
                            }
                            continue;
                        }
    
                        if (tmp_state == 5 && tmp_buf[tmp_pos] == ':')
                        {
                            tmp_state = 6, State = IDLE;
                            tmp_buf[tmp_pos + 1] = ''; // lock tmp_buf
                            // printk("%s | is `IPD` tmp_bak %d tmp_len %d command %s
    ", __func__, tmp_bak, tmp_len, tmp_buf + tmp_bak);
                            char *index = strstr((char *)tmp_buf + tmp_bak + 5 /* 5 > '+IPD,' and `+IPD,325:` in tmp_buf */, ",");
                            int ret = 0, len = 0;
                            if (index) { // '+IPD,3,1452:'
                                ret = sscanf((char *)tmp_buf + tmp_bak, "+IPD,%hhd,%d:", &mux_id, &len);
                                if (ret != 2 || mux_id < 0 || mux_id > 4 || len <= 0) {
                                    ; // Misjudge or fail, return, or clean up later
                                } else {
                                    tmp_len = tmp_bak, tmp_bak = 0; // Clean up the commands in the buffer and roll back the data
                                    frame_len = len, State = DATA;
                                }
                            } else { // '+IPD,1452:'
                                ret = sscanf((char *)tmp_buf + tmp_bak, "+IPD,%d:", &len);
                                if (ret != 1 || len <= 0) {
                                    ; // Misjudge or fail, return, or clean up later
                                } else {
                                    tmp_len = tmp_bak, tmp_bak = 0; // Clean up the commands in the buffer and roll back the data
                                    frame_len = len, State = DATA;
                                }
                            }
                            continue;
                        }
    

    由于这是第三次重构的代码,所以里面加了很多保护措施,但实际上这些措施不一定会遇到,它会随着各种高频传输调试中触发,代码要能够抵抗这些异常数据。

    我们看到 if (tmp_buf[tmp_pos] == ("+IPD,")[tmp_pos - tmp_bak]) { 的循环表示的就是 strstr 的匹配,但这里的匹配我稍微偷懒了一下简写了代码。

                        if (0 < tmp_state && tmp_state < 5) {
                            // printk("(%d, %02X) [%d, %02X]
    ", tmp_pos, tmp_buf[tmp_pos], tmp_pos - tmp_bak, ("+IPD,")[tmp_pos - tmp_bak]);
                            if (tmp_buf[tmp_pos] == ("+IPD,")[tmp_pos - tmp_bak]) {
                                tmp_state += 1; // tmp_state 1 + "IPD," to tmp_state 5
                            } else {
                                tmp_state = 0, State = IDLE;
                            }
                            continue;
                        }
    

    因为整个环境没有基础容器的支持,例如队列,所以我只能通过代码中的索引去模拟队列执行,保障整个执行的有序状态,当匹配完成后,会从 IPD 进入 DATA 态,此时意味着之后的数据都被视为数据,长度以 IPD 指示的为准。

                                    tmp_len = tmp_bak, tmp_bak = 0; // Clean up the commands in the buffer and roll back the data
                                    frame_len = len, State = DATA;
    

    进入 DATA 态的时候就要思考终止条件的状态了,有两种可能,一种是标识的长度数据走完了,这表示应该要转移状态到 ILDE 了,但期望从这个状态中发现终止条件。

                        if (frame_len < 0) {
                            if (frame_len == -1 && tmp_buf[tmp_pos] == 'C') { // wait "CLOSED
    "
                                frame_bak = frame_len;
                                continue;
                            }
                            if (tmp_state == 6 && tmp_buf[tmp_pos] == '
    ') {
                                tmp_state = 7;
                                continue;
                            }
                            if (tmp_state == 7 && tmp_buf[tmp_pos] == '
    ') {
                                if (frame_len == -2) { // match +IPD EOF (
    )
                                    tmp_state = 0, State = IDLE;
                                    // After receive complete, confirm the data is enough
                                    size = Buffer_Size(&nic->buffer);
                                    // printk("%s | size %d out_buff_len %d
    ", __func__, size, out_buff_len);
                                    if (size >= out_buff_len) { // data enough
                                        // printk("%s | recv out_buff_len overflow
    ", __func__);
                                        State = EXIT;
                                    }
                                } else if (frame_len == -8 && frame_len == -1)  {
                                    // Get "CLOSED
    "
                                    peer_just_closed = *peer_closed = true;
                                    frame_bak = 0, tmp_state = 0, State = EXIT;
                                } else {
                                    tmp_state = 6;
                                }
                                continue;
                            }
                            // 存在异常,没有得到 
     的匹配,并排除 CLOSED
     的指令触发的可能性,意味着传输可能越界出错了 
     ,则立即回到空闲状态。
                            if (frame_len <= -1 && frame_bak != -1) {
                                // printk("%s | tmp_state %d frame_len %d tmp %02X
    ", __func__, tmp_state, frame_len, tmp_buf[tmp_pos]);
                                State = IDLE;
                                continue;
                            }
                        }
    

    如我所假设的两种状态 和 CLOSED 这两类,但还有最终保障措施,假设无法符合我的预期的结尾,要尽快回归 ILDE 重新进入匹配,否则数据将一片混乱,这个只能保障 1 、 2 字节的误差,对于缓冲区溢出复写的情况无法抵抗。

    到这里我们还要注意到很多地方可能会出错和溢出,比如有如下考虑:

    • 上层 Python 接收数据的变量溢出,无法继续 read 更多,积压数据未处理。
    • 原始串口缓冲区溢出后,循环缓冲复写。
    • 内部状态机解析的数据缓冲长度(已经被保护了,但出错了会导致其中的一帧丢失)
    • 状态机解析后的数据缓冲变量写入可能会失败,夹在 C 与 Python 之间交互的一层缓冲区,主要用来供应 socket 层的数据。

    因此这里面的任何一个环境出错,都可能导致 HTTP 的原始下载数据出错,但所辛都可以完整下好不会出现指令与二进制数据混淆的情况了。

    基于这个接收框架,还可以进一步的拓展出 多 socket 的通信 和 其他 AT 指令的通信接口,匹配状态和解析后分类到各自对应的容器即可。

    旧接口函数抵抗异常的设计不够多,所以之后测试完整了后,可以基于新的接口继续发展新功能。

    我的测试结果如下:

    • 在串口波特率 921600(1M)约 92 kb/s 的下载速度下载 384kb kmodel 模型文件的过程。

      • 若是不写入 SD 卡的基础上,完美接收完毕,耗时 8s 。
      • 若是写入我的 256M 的路边野卡,完整下载时 10s,无任何损坏,但受到网络波动则会出现下载的文件中会存在失败的帧,下载过程的好坏参半。
    • 在串口波特率 576000(5M)约 57 kb/s 的下载速度下载 5M kmodel 模型文件的过程。

      • 写入SD卡的过程,下载 5M 文件无错且完整,较为稳定,少有失败的情况。

    从这个测试的结果来看,将 SD 卡写入数据是一个影响很大的变数,要么将其读写分离加缓冲在其中,减少 SD 卡带来的负面影响,优先把 HTTP 的数据带回内存之中。

    这和串口缓冲区有关,如现在假定的 10k 字节,而 921600 (92kb/s),意味着 read 数据要在 10k/x:100kb/s, x = 0.1s 内取走全部数据,否则 IO 不匹配,缓冲区会溢出,之后的数据都会被放弃从而形成接收数据的断层。

    python 的 code 执行模型如下

    while True:
        tmp = wifi.read()
        file.write(tmp)
    

    所以再下一次的 wifi.read() 因符合下述读写分离模型,这只是伪代码。不用太计较。

    # thread 0 
    while True:
        tmp = wifi.read()
        sleep(0.1)
    
    # thread 1
    while True:
        file.write(tmp)
        sleep(x)
    

    这次我们必须在硬件的内存和 SD 卡的写入数据中做一个选择,至少保证 SD 写入速度应大于 100kb/s 从而保持 IO 的速率基本一致,减少下载文件的异常。

    其他可能存在的问题,就比如有时候发送数据会请求失败,还不知道是哪个死角没考虑到,也许之后就会有人发现了吧?

    之后的内容都是代码的备份,分为 新接口函数,旧接口函数,对应的 Python 测试代码,有兴趣的可以自己调试试试。

    
    /*----------------------------------------------------------------------------*/
    /* +IPD,<id>,<len>:<data> */
    /* +IPD,<len>:<data> */
    /**
     * 
     * @return -1: parameters error, -2: EOF, -3: timeout, -4:peer closed and no data in buffer
     */
    uint32_t recvPkg(esp8285_obj *nic, char *out_buff, uint32_t out_buff_len, uint32_t *data_len, uint32_t timeout, char *coming_mux_id, bool *peer_closed, bool first_time_recv)
    {
        // printk("%s | head obl %d *dl %d tu %d ftr %d 
    ", __func__, out_buff_len, *data_len, timeout, first_time_recv);
    
        int err = 0, size = 0;
        bool peer_just_closed = false;
    
        // only for single socket but need socket map & buffer TODO:
        const mp_stream_p_t *uart_stream = mp_get_stream(nic->uart_obj);
        static int8_t mux_id = -1;
        static int32_t frame_sum = 0, frame_len = 0, frame_bak = 0;
    
        // parameters check
        if (out_buff == NULL) {
            return -1;
        }
        
        if (first_time_recv) {
            frame_sum = frame_len = 0;
        }
    
        // required data already in buf, just return data
        size = Buffer_Size(&nic->buffer);
        if (size >= out_buff_len) {
            Buffer_Gets(&nic->buffer, (uint8_t *)out_buff, out_buff_len);
            if (data_len) {
                *data_len = out_buff_len;
            }
            size = size > out_buff_len ? out_buff_len : size;
            frame_sum -= size;
            // printk("Buffer_Size frame_sum %d size %d *data_len %d
    ", frame_sum, size, *data_len);
            if (frame_sum <= 0) {
                Buffer_Clear(&nic->buffer);
                if (*peer_closed) { //buffer empty, return EOF
                    return -2;
                }
            }
            return out_buff_len;
        }
    
        enum fsm_state { IDLE, IPD, DATA, EXIT } State = IDLE;
        static uint8_t tmp_buf[1560 * 2] = {0}; // tmp_buf as queue 1560
        uint32_t tmp_bak = 0, tmp_state = 0, tmp_len = 0, tmp_pos = 0;
        mp_uint_t interrupt = 0, uart_recv_len = 0, res = 0;
        while (State != EXIT) {
            // wait any uart data
            uart_recv_len = uart_rx_any(nic->uart_obj);
            if (uart_recv_len > 0) {
    
                if (tmp_len >= sizeof(tmp_buf)) { // lock across
                    // printk("lock across frame_len %d tmp_len %d
    ", frame_len, tmp_len);
                    tmp_len = 0;
                }
    
                res = uart_stream->read(nic->uart_obj, tmp_buf + tmp_len, 1, &err);
                // printk("%s | tmp_buf %s res %d err %d
    ", __func__, tmp_buf, res, err);
                if (res == 1 && err == 0) {
                    
                    interrupt = mp_hal_ticks_ms();
                    // backup tmp_len to tmp_pos (tmp_pos - 1)
    
                    tmp_pos = tmp_len, tmp_len += 1; // buffer push
                    // printk("[%02X]", tmp_buf[tmp_pos]);
    
                    if (State == IDLE) {
                        if (tmp_buf[tmp_pos] == '+') {
                            tmp_state = 1, tmp_bak = tmp_pos, State = IPD;
                            continue;
                        } else {
                            // printk("(%02X)", tmp_buf[tmp_pos]);
                            tmp_len -= 1; // clear don't need data, such as (0D)(0A)
                            continue;
                        }
                    }
    
                    if (State == IPD) {
    
                        if (tmp_pos - tmp_bak > 12) { // Over the length of the '+IPD,3,1452:' or '+IPD,1452:'
                            tmp_state = 0, State = IDLE;
                            continue;
                        }
    
                        if (0 < tmp_state && tmp_state < 5) {
                            // printk("(%d, %02X) [%d, %02X]
    ", tmp_pos, tmp_buf[tmp_pos], tmp_pos - tmp_bak, ("+IPD,")[tmp_pos - tmp_bak]);
                            if (tmp_buf[tmp_pos] == ("+IPD,")[tmp_pos - tmp_bak]) {
                                tmp_state += 1; // tmp_state 1 + "IPD," to tmp_state 5
                            } else {
                                tmp_state = 0, State = IDLE;
                            }
                            continue;
                        }
    
                        if (tmp_state == 5 && tmp_buf[tmp_pos] == ':')
                        {
                            tmp_state = 6, State = IDLE;
                            tmp_buf[tmp_pos + 1] = ''; // lock tmp_buf
                            // printk("%s | is `IPD` tmp_bak %d tmp_len %d command %s
    ", __func__, tmp_bak, tmp_len, tmp_buf + tmp_bak);
                            char *index = strstr((char *)tmp_buf + tmp_bak + 5 /* 5 > '+IPD,' and `+IPD,325:` in tmp_buf */, ",");
                            int ret = 0, len = 0;
                            if (index) { // '+IPD,3,1452:'
                                ret = sscanf((char *)tmp_buf + tmp_bak, "+IPD,%hhd,%d:", &mux_id, &len);
                                if (ret != 2 || mux_id < 0 || mux_id > 4 || len <= 0) {
                                    ; // Misjudge or fail, return, or clean up later
                                } else {
                                    tmp_len = tmp_bak, tmp_bak = 0; // Clean up the commands in the buffer and roll back the data
                                    frame_len = len, State = DATA;
                                }
                            } else { // '+IPD,1452:'
                                ret = sscanf((char *)tmp_buf + tmp_bak, "+IPD,%d:", &len);
                                if (ret != 1 || len <= 0) {
                                    ; // Misjudge or fail, return, or clean up later
                                } else {
                                    tmp_len = tmp_bak, tmp_bak = 0; // Clean up the commands in the buffer and roll back the data
                                    frame_len = len, State = DATA;
                                }
                            }
                            continue;
                        }
                    }
    
                    if (State == DATA) {
                        // printk("%s | frame_len %d tmp_len %d tmp_buf[tmp_pos] %02X
    ", __func__, frame_len, tmp_len, tmp_buf[tmp_pos]);
                        
                        frame_len -= tmp_len, tmp_len = 0; // get data
    
                        if (frame_len < 0) {
                            if (frame_len == -1 && tmp_buf[tmp_pos] == 'C') { // wait "CLOSED
    "
                                frame_bak = frame_len;
                                continue;
                            }
                            if (tmp_state == 6 && tmp_buf[tmp_pos] == '
    ') {
                                tmp_state = 7;
                                continue;
                            }
                            if (tmp_state == 7 && tmp_buf[tmp_pos] == '
    ') {
                                if (frame_len == -2) { // match +IPD EOF (
    )
                                    tmp_state = 0, State = IDLE;
                                    // After receive complete, confirm the data is enough
                                    size = Buffer_Size(&nic->buffer);
                                    // printk("%s | size %d out_buff_len %d
    ", __func__, size, out_buff_len);
                                    if (size >= out_buff_len) { // data enough
                                        // printk("%s | recv out_buff_len overflow
    ", __func__);
                                        State = EXIT;
                                    }
                                } else if (frame_len == -8 && frame_len == -1)  {
                                    // Get "CLOSED
    "
                                    peer_just_closed = *peer_closed = true;
                                    frame_bak = 0, tmp_state = 0, State = EXIT;
                                } else {
                                    tmp_state = 6;
                                }
                                continue;
                            }
                            // 存在异常,没有得到 
     的匹配,并排除 CLOSED
     的指令触发的可能性,意味着传输可能越界出错了 
     ,则立即回到空闲状态。
                            if (frame_len <= -1 && frame_bak != -1) {
                                // printk("%s | tmp_state %d frame_len %d tmp %02X
    ", __func__, tmp_state, frame_len, tmp_buf[tmp_pos]);
                                State = IDLE;
                                continue;
                            }
                        } else {
                            // for(int i = 0; i < tmp_len; i++) {
                            //     int tmp = tmp_buf[i];
                            //     printk("[%02X]", tmp);
                            // }
                            // printk("%s | frame_len %d tmp_len %d
    ", __func__, frame_len, tmp_pos + 1);
                            // printk("%.*s", tmp_len, tmp_buf);
                            if (!Buffer_Puts(&nic->buffer, tmp_buf, (tmp_pos + 1))) {
                                printk("%s | network->buffer overflow Buffer Max %d Size %d
    ", __func__, ESP8285_BUF_SIZE, Buffer_Size(&nic->buffer));
                                State = EXIT;
                            } else {
                                // reduce data len
                                // printk("[%02X]", tmp_buf[(tmp_pos + 1)]);
                                frame_sum += (tmp_pos + 1);
                            }
                            // printk("frame_sum %d frame_len %d tmp_len %d
    ", frame_sum, frame_len, tmp_pos + 1);
                        }
                        continue;
                    }
    
                } else {
                    State = EXIT;
                }
            }
            
            if (mp_hal_ticks_ms() - interrupt > timeout) {
                break; // uart no return data to timeout break
            }
    
            if (*peer_closed) {
                break; // disconnection
            }
        }
    
        size = Buffer_Size(&nic->buffer);
        
        // peer closed and no data in buffer
        if (size == 0 && !peer_just_closed && *peer_closed) {
            frame_sum = 0;
            return -4;
        }
    
        size = size > out_buff_len ? out_buff_len : size;
        
        Buffer_Gets(&nic->buffer, (uint8_t *)out_buff, size);
        if (data_len) {
            *data_len = size;
        }
    
        frame_sum -= size;
    
        if (frame_sum <= 0 || peer_just_closed) {
            Buffer_Clear(&nic->buffer);
            if (peer_just_closed)
            {
                frame_sum = 0;
                return -2;
            }
        }
    
        // printk(" %s | tail obl %d *dl %d se %d
    ", __func__, out_buff_len, *data_len, size);
    
        return size;
    }
    

    肝了三天了,彻底优化了 AT 8285 的通信过程,等测试得差不多了再提交,目前只测到了波特率 576000 下 HTTP 下载 K-Flash.zip (5M)二进制文件,数据完整,接收过程大体是没有太大问题了。

    备份旧 CODE 。

    
    uint32_t bak_recvPkg(esp8285_obj *nic, char *out_buff, uint32_t out_buff_len, uint32_t *data_len, uint32_t timeout, char *coming_mux_id, bool *peer_closed, bool first_time_recv)
    {
          
        const mp_stream_p_t *uart_stream = mp_get_stream(nic->uart_obj);
        static uint8_t temp_buff1[2048] = {0};
        static uint8_t temp_buff2[2048] = {0};
        uint16_t temp_buff1_len = 0;
        uint16_t temp_buff2_len = 0;
        uint8_t find_frame_flag_index = 0;
        static int8_t mux_id = -1;
        static int16_t frame_len = 0;
        static int32_t frame_len_sum = 0; //only for single socket TODO:
        // bool    overflow = false;
        int ret = 0;
        int size = 0;
        int errcode;
        mp_uint_t start1 = 0, start2 = 0;
        bool no_frame_flag = false;
        bool new_frame = false;
        mp_uint_t data_len_in_uart_buff = 0;
        bool peer_just_closed = false;
    
        // parameters check
        if (out_buff == NULL)
        {
            return -1;
        }
        // // printk("
     if (out_buff == NULL) { 
    ");
        // init vars
        memset(temp_buff1, 0, sizeof(temp_buff1));
        memset(temp_buff2, 0, sizeof(temp_buff2));
        if (first_time_recv)
        {
            frame_len = 0;
            frame_len_sum = 0;
        }
    
        // required data already in buf, just return data
        uint32_t buff_size = Buffer_Size(&nic->buffer);
        // // printk("
     if(buff_size >= out_buff_len) 
    ");
    
        // printk("
     buff_size %d out_buff_len %d
    ", buff_size, out_buff_len);
    
        if (buff_size >= out_buff_len)
        {
            Buffer_Gets(&nic->buffer, (uint8_t *)out_buff, out_buff_len);
            if (data_len)
                *data_len = out_buff_len;
            frame_len_sum -= size;
            if (frame_len_sum <= 0)
            {
                frame_len = 0;
                frame_len_sum = 0;
                Buffer_Clear(&nic->buffer);
                if (*peer_closed) //buffer empty, return EOF
                {
                    return -2;
                }
            }
            return out_buff_len;
        }
    
        // read from uart buffer, if not frame start flag, put into nic buffer
        // and need wait for full frame flag in 200ms(can be fewer), frame format: '+IPD,id,len:data' or '+IPD,len:data'
        // wait data from uart buffer if not timeout
        start2 = mp_hal_ticks_ms();
        data_len_in_uart_buff = uart_rx_any(nic->uart_obj);
        // // printk("
     uart_stream->read %p 
    ", uart_stream->read);
    
        do
        {
            if (data_len_in_uart_buff > 0)
            {
                uart_stream->read(nic->uart_obj, temp_buff1 + temp_buff1_len, 1, &errcode);
                if (find_frame_flag_index == 0 && temp_buff1[temp_buff1_len] == '+')
                {
                    ++find_frame_flag_index;
                    start1 = mp_hal_ticks_ms();
                }
                else if (find_frame_flag_index == 1 && temp_buff1[temp_buff1_len] == 'I')
                {
                    ++find_frame_flag_index;
                }
                else if (find_frame_flag_index == 2 && temp_buff1[temp_buff1_len] == 'P')
                {
                    ++find_frame_flag_index;
                }
                else if (find_frame_flag_index == 3 && temp_buff1[temp_buff1_len] == 'D')
                {
                    ++find_frame_flag_index;
                }
                else if (find_frame_flag_index == 4 && temp_buff1[temp_buff1_len] == ',')
                {
                    ++find_frame_flag_index;
                }
                else if (find_frame_flag_index == 5)
                {
                    if (temp_buff1[temp_buff1_len] == ':')
                    { // '+IPD,3,1452:' or '+IPD,1452:'
                        temp_buff1[temp_buff1_len + 1] = '';
                        char *index = strstr((char *)temp_buff1 + 5, ",");
                        if (index)
                        { // '+IPD,3,1452:'
                            ret = sscanf((char *)temp_buff1, "+IPD,%hhd,%hd:", &mux_id, &frame_len);
                            if (ret != 2 || mux_id < 0 || mux_id > 4 || frame_len <= 0)
                            { // format not satisfy, it's data
                                no_frame_flag = true;
                            }
                            else
                            { // find frame start flag, although it may also data
                                new_frame = true;
                            }
                        }
                        else
                        { // '+IPD,1452:'
                            ret = sscanf((char *)temp_buff1, "+IPD,%hd:", &frame_len);
                            if (ret != 1 || frame_len <= 0)
                            { // format not satisfy, it's data
                                no_frame_flag = true;
                            }
                            else
                            { // find frame start flag, although it may also data
                                new_frame = true;
                                // // printk("new frame:%d
    ", frame_len);
                            }
                        }
                    }
                }
                else
                { // not match frame start flag, put into nic buffer
                    no_frame_flag = true;
                }
                // new frame or data
                // or wait for frame start flag timeout(300ms, can be fewer), maybe they're data
                if (new_frame || no_frame_flag || temp_buff1_len >= 12 ||
                    (find_frame_flag_index && (mp_hal_ticks_ms() - start1 > 300))) // '+IPD,3,1452:'
                {
                    if (!new_frame)
                    {
                        if (frame_len_sum > 0)
                        {
                            // // printk("if(frame_len_sum > 0) { temp_buff2_len-%d temp_buff1_len-%d
    ", temp_buff2_len, temp_buff1_len);
                            if (!Buffer_Puts(&nic->buffer, temp_buff1, temp_buff1_len + 1))
                            {
                                // printk("data_len_in_uart_buff %u temp_buff1_len %u temp_buff1 ", data_len_in_uart_buff, temp_buff1_len);
                                for (int i = 0; i < temp_buff1_len; i++) {
                                    int tmp = temp_buff1[i];
                                    // printk("(%c %02X)", temp_buff1[i], tmp);
                                }
                                // printk("
    ");
                                // printk("data_len_in_uart_buff %u temp_buff1_len %u temp_buff1 ", data_len_in_uart_buff, temp_buff1_len);
                                for (int i = 0; i < temp_buff1_len; i++) {
                                    int tmp = temp_buff1[i];
                                    // printk("(%c %02X)", temp_buff1[i], tmp);
                                }
                                // printk("
    ");
                                // overflow = true;
                                // break;//TODO:
                            }
                        }
                        else
                        {
                            if (temp_buff1[0] == 'C')
                            {
                                memset(temp_buff2, 0, sizeof(temp_buff2));
                            }
    
                            // // printk("-%d:%s
    ", temp_buff2_len, temp_buff2);
    
                            // // printk("!(if(frame_len_sum > 0) {) temp_buff2_len-%d temp_buff1_len-%d
    ", temp_buff2_len, temp_buff1_len);
                            temp_buff2[temp_buff2_len++] = temp_buff1[0];
                            // // printk("%c", temp_buff1[0]); //TODO: optimize uart overflow, if uart overflow, uncomment this will print some data
                            // // printk("-%d:%s
    ", temp_buff2_len, temp_buff2);
                            if (strstr((const char *)temp_buff2, "CLOSED
    ") != NULL)
                            {
                                // // printk("pear closed
    ");
                                *peer_closed = true;
                                peer_just_closed = true;
                                break;
                            }
                        }
                    }
                    else
                    {
                        frame_len_sum += frame_len;
                    }
                    find_frame_flag_index = 0;
                    temp_buff1_len = 0;
                    new_frame = false;
                    no_frame_flag = false;
                    // enough data as required
                    size = Buffer_Size(&nic->buffer);
                    if (size >= out_buff_len) // data enough
                        break;
                    if (frame_len_sum != 0 && frame_len_sum <= size) // read at least one frame ok
                    {
                        break;
                    }
                    continue;
                }
                ++temp_buff1_len;
            }
            if (timeout != 0 && (mp_hal_ticks_ms() - start2 > timeout) && !find_frame_flag_index)
            {
                // // printk("
     timeout %d start2 %d (mp_hal_ticks_ms() - start2 > timeout) %d find_frame_flag_index %d 
    ", timeout, start2, (mp_hal_ticks_ms() - start2 > timeout), find_frame_flag_index);
                // // printk("
    -3 recvPkg nic %p out_buff %p out_buff_len %p data_len %p timeout %p coming_mux_id %p peer_closed %p first_time_recv %p
    ", nic, out_buff, &out_buff_len, data_len, &timeout, coming_mux_id, peer_closed, &first_time_recv);
                // printk("data_len_in_uart_buff %u temp_buff1_len %u temp_buff1 ", data_len_in_uart_buff, temp_buff1_len);
                for (int i = 0; i < temp_buff1_len; i++) {
                    int tmp = temp_buff1[i];
                    // printk("(%c %02X)", temp_buff1[i], tmp);
                }
                // printk("
    ");
                return -3;
            }
            data_len_in_uart_buff = uart_rx_any(nic->uart_obj);
    
            // // printk("
    2-%hd 1-%hd
    ", temp_buff2_len, temp_buff1_len);
    
        } while ((timeout || find_frame_flag_index) && (!*peer_closed || data_len_in_uart_buff > 0));
    
        size = Buffer_Size(&nic->buffer);
        if (size == 0 && !peer_just_closed && *peer_closed) //peer closed and no data in buffer
        {
            frame_len = 0;
            frame_len_sum = 0;
            // // printk("
    -4 recvPkg nic %p out_buff %p out_buff_len %p data_len %p timeout %p coming_mux_id %p peer_closed %p first_time_recv %p
    ", nic, out_buff, &out_buff_len, data_len, &timeout, coming_mux_id, peer_closed, &first_time_recv);
            return -4;
        }
        size = size > out_buff_len ? out_buff_len : size;
        Buffer_Gets(&nic->buffer, (uint8_t *)out_buff, size);
        if (data_len)
            *data_len = size;
        frame_len_sum -= size;
        if (frame_len_sum <= 0 || peer_just_closed)
        {
            frame_len = 0;
            frame_len_sum = 0;
            Buffer_Clear(&nic->buffer);
            if (peer_just_closed)
            {
                // // printk("
    -2 recvPkg nic %p out_buff %p out_buff_len %p data_len %p timeout %p coming_mux_id %p peer_closed %p first_time_recv %p
    ", nic, out_buff, &out_buff_len, data_len, &timeout, coming_mux_id, peer_closed, &first_time_recv);
                return -2;
            }
        }
    
        // mp_printf(&mp_plat_print, "[MaixPy] %s | size %d out_buff_len %d *data_len %d 
    ", __func__, size, out_buff_len, *data_len);
        return size;
    }
    
    

    旧 code 最大的问题在于处理过程单向思维,新 code 主要是上状态机分离 AT 指令和数据的处理过程。

    对应的 HTTP Download 的 Python Code 也备份一下。

    """
    The MIT License (MIT)
    Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
    """
    
    from struct import pack
    import socket
    import gc
    
    
    class MicroWebCli:
    
        # ============================================================================
        # ===( Class AuthBasic  )=====================================================
        # ============================================================================
    
        class AuthBasic:
    
            # ------------------------------------------------------------------------
    
            def __init__(self, user, password):
                if password is None:
                    password = ''
                if not 'b2a_base64' in globals():
                    from binascii import b2a_base64
                cred = '%s:%s' % (user, password)
                self._auth = 'Basic %s' % b2a_base64(
                    cred.encode()).decode().strip()
    
            # ------------------------------------------------------------------------
    
            def Apply(self, microWebCli):
                microWebCli.Headers['Authorization'] = self._auth
    
        # ============================================================================
        # ===( Class AuthToken  )=====================================================
        # ============================================================================
    
        class AuthToken:
    
            # ------------------------------------------------------------------------
    
            def __init__(self, token):
                self._auth = 'Bearer %s' % token
    
            # ------------------------------------------------------------------------
    
            def Apply(self, microWebCli):
                microWebCli.Headers['Authorization'] = self._auth
    
        # ============================================================================
        # ===( Utils  )===============================================================
        # ============================================================================
    
        def _tryAllocByteArray(size):
            for x in range(10):
                try:
                    gc.collect()
                    return bytearray(size)
                except:
                    pass
            return None
    
        # ----------------------------------------------------------------------------
    
        @staticmethod
        def _quote(s, safe='/'):
            r = ''
            for c in str(s):
                if (c >= 'a' and c <= 'z') or 
                   (c >= '0' and c <= '9') or 
                   (c >= 'A' and c <= 'Z') or 
                   (c in '.-_') or (c in safe) :
                    r += c
                else:
                    for b in c.encode('UTF-8'):
                        r += '%%%02X' % b
            return r
    
        # ----------------------------------------------------------------------------
    
        @staticmethod
        def _urlEncode(s):
            return MicroWebCli._quote(s, ';/?:@&=+$,')
    
        # ----------------------------------------------------------------------------
    
        @staticmethod
        def _unquote(s):
            r = str(s).split('%')
            try:
                b = r[0].encode()
                for i in range(1, len(r)):
                    try:
                        b += bytes([int(r[i][:2], 16)]) + r[i][2:].encode()
                    except:
                        b += b'%' + r[i].encode()
                return b.decode('UTF-8')
            except:
                return str(s)
    
        # ----------------------------------------------------------------------------
    
        @staticmethod
        def _unquote_plus(s):
            return MicroWebCli._unquote(str(s).replace('+', ' '))
    
        # ----------------------------------------------------------------------------
    
        def GETRequest(url,
                       queryParams=None,
                       auth=None,
                       connTimeoutSec=10,
                       socks5Addr=None):
            c = MicroWebCli(url,
                            auth=auth,
                            connTimeoutSec=connTimeoutSec,
                            socks5Addr=socks5Addr)
            if queryParams:
                c.QueryParams = queryParams
            c.OpenRequest()
            r = c.GetResponse()
            if r.IsSuccess():
                return r.ReadContent()
            r.Close()
            if r.IsLocationMoved():
                return MicroWebCli.GETRequest(r.LocationMovedURL(), queryParams,
                                              auth, connTimeoutSec, socks5Addr)
            return None
    
        # ----------------------------------------------------------------------------
    
        def POSTRequest(url,
                        formData={},
                        auth=None,
                        connTimeoutSec=10,
                        socks5Addr=None):
            c = MicroWebCli(url,
                            method='POST',
                            auth=auth,
                            connTimeoutSec=connTimeoutSec,
                            socks5Addr=socks5Addr)
            c.OpenRequestFormData(formData)
            r = c.GetResponse()
            if r.IsSuccess():
                return r.ReadContent()
            r.Close()
            if r.IsLocationMoved():
                return MicroWebCli.POSTRequest(r.LocationMovedURL(), formData,
                                               auth, connTimeoutSec, socks5Addr)
            return None
    
        # ----------------------------------------------------------------------------
    
        def JSONRequest(url,
                        o=None,
                        auth=None,
                        connTimeoutSec=10,
                        socks5Addr=None):
            c = MicroWebCli(url,
                            method=('POST' if o else 'GET'),
                            auth=auth,
                            connTimeoutSec=connTimeoutSec,
                            socks5Addr=socks5Addr)
            if o:
                c.OpenRequestJSONData(o)
            else:
                c.OpenRequest()
            r = c.GetResponse()
            if r.IsSuccess():
                return r.ReadContentAsJSON()
            r.Close()
            if r.IsLocationMoved():
                return MicroWebCli.JSONRequest(r.LocationMovedURL(), o, auth,
                                               connTimeoutSec, socks5Addr)
            return None
    
        # ----------------------------------------------------------------------------
    
        def FileRequest(url,
                        filepath,
                        progressCallback=None,
                        auth=None,
                        connTimeoutSec=5,
                        socks5Addr=None):
            c = MicroWebCli(url,
                            auth=auth,
                            connTimeoutSec=connTimeoutSec,
                            socks5Addr=socks5Addr)
            c.OpenRequest()
            r = c.GetResponse()
            if r.IsSuccess():
                r.WriteContentToFile(filepath, progressCallback)
                return r.GetContentType()
            r.Close()
            if r.IsLocationMoved():
                return MicroWebCli.FileRequest(r.LocationMovedURL(), filepath,
                                               progressCallback, auth,
                                               connTimeoutSec, socks5Addr)
            return None
    
        # ============================================================================
        # ===( Constructor )==========================================================
        # ============================================================================
    
        def __init__(self,
                     url='',
                     method='GET',
                     auth=None,
                     connTimeoutSec=10,
                     socks5Addr=None):
            self.URL = url
            self.Method = method
            self.Auth = auth
            self.ConnTimeoutSec = connTimeoutSec
            self._socks5Addr = socks5Addr
            self._headers = {}
            self._socket = None
            self._socketAddr = None
            self._response = None
    
        # ============================================================================
        # ===( Functions )============================================================
        # ============================================================================
    
        def _write(self, data):
            try:
                data = memoryview(data)
                while data:
                    n = self._socket.write(data)
                    data = data[n:]
                return True
            except:
                self.Close()
                raise Exception('Error to send data on connection')
    
        # ------------------------------------------------------------------------
    
        def _writeFirstLine(self):
            path = MicroWebCli._quote(self.Path)
            qs = self.QueryString
            if qs != '':
                path = path + '?' + qs
            self._write('%s %s HTTP/1.0
    ' % (self.Method, path))
    
        # ------------------------------------------------------------------------
    
        def _writeHeader(self, name, value):
            self._write("%s: %s
    " % (name, value))
    
        # ------------------------------------------------------------------------
    
        def _writeEndHeader(self):
            self._write("
    ")
    
        # ------------------------------------------------------------------------
    
        def OpenRequest(self, data=None, contentType=None, contentLength=None):
            if self._socket:
                raise Exception('Request is already opened')
            if not self.URL:
                raise Exception('No URL defined')
            if self.Socks5Addr:
                if not isinstance(self.Socks5Addr,
                                  tuple) or len(self.Socks5Addr) != 2:
                    raise Exception('"Socks5Addr" must be a tuple of (host, port)')
                host, port = self.Socks5Addr
                if not isinstance(host, str) or not isinstance(port, int):
                    raise Exception('"Socks5Addr" is incorrect ("%s", %s)' %
                                    self.Socks5Addr)
            else:
                host = self.Host
                port = self.Port
            self._response = None
            try:
                err = 0
                while 1:
                    try:
                        self._socketAddr = socket.getaddrinfo(host, port)[0][-1]
                        break
                    except Exception:
                        err += 1
                    if err > 5:
                        raise Exception("get ip failed!")
                #cli = socket.socket()
                cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                cli.settimeout(self.ConnTimeoutSec)
                cli.connect(self._socketAddr)
            except:
                raise Exception('Error to connect to %s:%s' % (host, port))
            if self.Socks5Addr:
                err = None
                try:
                    cli.send(b'x05x01x00')
                    b = cli.read(2)
                    if b is None or len(b) < 2 or b[0] != 0x05 or b[1] != 0x00:
                        err = "%s:%s doesn't supports MicroWebCli SOCKS5 client protocol" % self.Socks5Addr
                    else:
                        h = self.Host.encode()
                        p = pack('>H', self.Port)
                        cli.send(b'x05x01x00x03' + bytes([len(h)]) + h + p)
                        b = cli.read(4)
                        if b is None or len(b) < 4 or b[1] != 0x00:
                            err = "Error to connect to %s:%s through SOCKS5 server" % (
                                self.Host, self.Port)
                        else:
                            if b[3] == 0x01:
                                l = 4
                            elif b[3] == 0x03:
                                l = cli.read(1)[0]
                            elif b[3] == 0x04:
                                l = 16
                            cli.read(l + 2)
                except Exception as ex:
                    err = 'Error during negotiation with SOCKS5 server (%s)' % ex
                if err:
                    cli.close()
                    raise Exception(err)
            if self.Proto == 'https':
                if not 'ssl' in globals():
                    import ssl
                try:
                    try:
                        cli = ssl.wrap_socket(cli, timeout=self.ConnTimeoutSec)
                    except TypeError:
                        cli = ssl.wrap_socket(cli)
                except Exception as ex:
                    cli.close()
                    raise Exception(
                        'Error to open a secure SSL/TLS connection (%s)' % ex)
            self._socket = cli
            self._writeFirstLine()
            if data:
                contentLength = len(data)
            self._headers['Host'] = self.Host
            if self._auth:
                try:
                    self._auth.Apply(self)
                except:
                    raise Exception('Error to apply authentication using %s' %
                                    type(self._auth))
            else:
                self._headers.pop('Authorization', None)
            if contentType:
                self._headers['Content-Type'] = contentType
            else:
                self._headers.pop('Content-Type', None)
            if contentLength:
                self._headers['Content-Length'] = contentLength
            else:
                self._headers.pop('Content-Length', None)
            self._headers['User-Agent'] = 'MicroWebCli by JC`zic'
            for h in self._headers:
                self._writeHeader(h, self._headers[h])
            self._writeEndHeader()
            if data:
                self._write(data)
    
        # ------------------------------------------------------------------------
    
        def OpenRequestFormData(self, formData={}):
            data = ''
            if len(formData) > 0:
                for param in formData:
                    if param != '':
                        if data != '':
                            data += '&'
                        data += MicroWebCli._quote(
                            param) + '=' + MicroWebCli._quote(formData[param])
            self.OpenRequest(data=data,
                             contentType='application/x-www-form-urlencoded')
    
        # ------------------------------------------------------------------------
    
        def OpenRequestJSONData(self, o=None):
            if not 'json' in globals():
                import json
            try:
                data = json.dumps(o)
            except:
                raise Exception('Error to convert object to JSON format')
            self.OpenRequest(data=data, contentType='application/json')
    
        # ------------------------------------------------------------------------
    
        def RequestWriteData(self, data):
            self._write(data)
    
        # ------------------------------------------------------------------------
    
        def GetResponse(self):
            if not self._response:
                self._response = MicroWebCli._response(self, self._socket,
                                                       self._socketAddr)
            return self._response
    
        # ------------------------------------------------------------------------
    
        def IsClosed(self):
            return self._socket is None
    
        # ------------------------------------------------------------------------
    
        def Close(self):
            if self._socket:
                try:
                    self._socket.close()
                except:
                    pass
                self._socket = None
    
        # ============================================================================
        # ===( Properties )===========================================================
        # ============================================================================
    
        @property
        def ConnTimeoutSec(self):
            return self._connTimeoutSec
    
        @ConnTimeoutSec.setter
        def ConnTimeoutSec(self, value):
            self._connTimeoutSec = int(value) if value and int(value) > 0 else None
    
        # ------------------------------------------------------------------------
    
        @property
        def Method(self):
            return self._method
    
        @Method.setter
        def Method(self, value):
            self._method = str(value).upper()
    
        # ------------------------------------------------------------------------
    
        @property
        def URL(self):
            host = self.Host
            if host != '':
                proto = self.Proto
                port = self.Port
                if ( proto == 'http'  and port == 80  ) or 
                   ( proto == 'https' and port == 443 ) :
                    port = ''
                else:
                    port = ':' + str(port)
                url = proto + '://' + host + port + self.Path
                url = MicroWebCli._urlEncode(url)
                qs = self.QueryString
                if qs != '':
                    return url + '?' + qs
                return url
            return None
    
        @URL.setter
        def URL(self, value):
            try:
                s = str(value)
                if '://' in s:
                    proto, s = s.split('://', 1)
                else:
                    proto = 'http'
            except:
                raise ValueError('URL error (%s)' % value)
            self.Proto = proto
            if '/' in s:
                host, path = s.split('/', 1)
            elif '?' in s:
                host, path = s.split('?', 1)
                path = '?' + path
            else:
                host = s
                path = ''
            if ':' in host:
                try:
                    host, port = host.split(':')
                    self.Port = port
                except:
                    raise ValueError('URL host:port error (%s)' % host)
            self.Host = host
            self._queryParams = {}
            self.Path = path
    
        # ------------------------------------------------------------------------
    
        @property
        def Proto(self):
            return self._proto
    
        @Proto.setter
        def Proto(self, value):
            value = str(value).lower()
            if value == 'http':
                self._port = 80
            elif value == 'https':
                self._port = 443
            else:
                raise ValueError('Unsupported URL protocol (%s)' % value)
            self._proto = value
    
        # ------------------------------------------------------------------------
    
        @property
        def Host(self):
            return self._host
    
        @Host.setter
        def Host(self, value):
            self._host = MicroWebCli._unquote_plus(str(value))
    
        # ------------------------------------------------------------------------
    
        @property
        def Port(self):
            return self._port
    
        @Port.setter
        def Port(self, value):
            self._port = int(value)
    
        # ------------------------------------------------------------------------
    
        @property
        def Path(self):
            return self._path
    
        @Path.setter
        def Path(self, value):
            x = value.split('?', 1)
            if len(x[0]) > 0:
                if x[0][0] != '/':
                    x[0] = '/' + x[0]
                self._path = MicroWebCli._unquote_plus(x[0])
            else:
                self._path = '/'
            if len(x) == 2:
                self.QueryString = x[1]
    
        # ------------------------------------------------------------------------
    
        @property
        def QueryString(self):
            r = ''
            for param in self._queryParams:
                if param != '':
                    if r != '':
                        r += '&'
                    r += MicroWebCli._quote(param) + '=' + MicroWebCli._quote(
                        self._queryParams[param])
            return r
    
        @QueryString.setter
        def QueryString(self, value):
            self._queryParams = {}
            for x in value.split('&'):
                param = x.split('=', 1)
                if param[0] != '':
                    value = MicroWebCli._unquote(
                        param[1]) if len(param) > 1 else ''
                    self._queryParams[MicroWebCli._unquote(param[0])] = value
    
        # ------------------------------------------------------------------------
    
        @property
        def QueryParams(self):
            return self._queryParams
    
        @QueryParams.setter
        def QueryParams(self, value):
            if not isinstance(value, dict):
                raise ValueError('QueryParams must be a dict')
            self._queryParams = value
    
        # ------------------------------------------------------------------------
    
        @property
        def Headers(self):
            return self._headers
    
        @Headers.setter
        def Headers(self, value):
            if not isinstance(value, dict):
                raise ValueError('Headers must be a dict')
            self._headers = value
    
        # ------------------------------------------------------------------------
    
        @property
        def Auth(self):
            return self._auth
    
        @Auth.setter
        def Auth(self, value):
            self._auth = value
    
        # ------------------------------------------------------------------------
    
        @property
        def Socks5Addr(self):
            return self._socks5Addr
    
        @Socks5Addr.setter
        def Socks5Addr(self, value):
            self._socks5Addr = value
    
        # ============================================================================
        # ===( Class Response  )======================================================
        # ============================================================================
    
        class _response:
    
            # ------------------------------------------------------------------------
    
            def __init__(self, microWebCli, socket, addr):
                self._microWebCli = microWebCli
                self._socket = socket
                self._addr = addr
                self._httpVer = None
                self._code = None
                self._msg = None
                self._headers = {}
                self._contentType = None
                self._contentLength = None
                self._processResponse()
    
            # ------------------------------------------------------------------------
    
            def _processResponse(self):
                try:
                    self._parseFirstLine()
                    self._parseHeader()
                    if self._contentLength == 0:
                        self.Close()
                except:
                    self._microWebCli.Close()
                    raise Exception('Error to get response')
    
            # ------------------------------------------------------------------------
    
            def _parseFirstLine(self):
                tmp = self._socket.readline();
                print(tmp)
                self._httpVer, code, self._msg = tmp 
                                         .decode()   
                                         .strip()    
                                         .split(' ', 2)
                self._code = int(code)
    
            # ------------------------------------------------------------------------
    
            def _parseHeader(self):
                while True:
                    tmp = self._socket.readline();
                    print(tmp)
                    elements = tmp 
                       .decode()   
                       .strip()    
                       .split(':', 1)
                    if len(elements) == 2:
                        self._headers[elements[0].strip()] = elements[1].strip()
                    elif len(elements) == 1 and len(elements[0]) == 0:
                        self._contentType = self._headers.get("Content-Type", None)
                        ctLen = self._headers.get("Content-Length", None)
                        self._contentLength = int(
                            ctLen) if ctLen is not None else None
                        break
    
            # ------------------------------------------------------------------------
    
            def GetClient(self):
                return self._microWebCli
    
            # ------------------------------------------------------------------------
    
            def GetAddr(self):
                return self._addr
    
            # ------------------------------------------------------------------------
    
            def GetIPAddr(self):
                return self._addr[0]
    
            # ------------------------------------------------------------------------
    
            def GetPort(self):
                return self._addr[1]
    
            # ------------------------------------------------------------------------
    
            def GetHTTPVersion(self):
                return self._httpVer
    
            # ------------------------------------------------------------------------
    
            def GetStatusCode(self):
                return self._code
    
            # ------------------------------------------------------------------------
    
            def GetStatusMessage(self):
                return self._msg
    
            # ------------------------------------------------------------------------
    
            def IsSuccess(self):
                return (self._code >= 200 and self._code < 300)
    
            # ------------------------------------------------------------------------
    
            def IsLocationMoved(self):
                return self.LocationMovedURL() is not None
    
            # ------------------------------------------------------------------------
    
            def LocationMovedURL(self):
                if self._code >= 300 and self._code < 400:
                    return self._headers.get('Location', None)
                return None
    
            # ------------------------------------------------------------------------
    
            def GetHeaders(self):
                return self._headers
    
            # ------------------------------------------------------------------------
    
            def GetContentType(self):
                return self._contentType
    
            # ------------------------------------------------------------------------
    
            def GetContentLength(self):
                return self._contentLength
    
            # ------------------------------------------------------------------------
    
            def ReadContent(self, size=None):
                try:
                    if size is None:
                        b = self._socket.read()
                        self.Close()
                        return b
                    elif size > 0:
                        b = self._socket.read(size)
                        if len(b) < size:
                            self.Close()
                        return b
                except MemoryError as memEx:
                    self.Close()
                    raise MemoryError('Error to read response content (%s)' %
                                      memEx)
                except:
                    self.Close()
                return None
    
            # ------------------------------------------------------------------------
    
            def ReadContentInto(self, buf, nbytes=None):
                if nbytes is None:
                    nbytes = len(buf)
                if nbytes > 0:
                    try:
                        x = self._socket.readinto(buf, nbytes)
                        if x < nbytes:
                            self.Close()
                        return x
                    except Exception as e:
                        print(e)
                        self.Close()
                    except:
                        self.Close()
                return 0
    
            # ------------------------------------------------------------------------
    
            def ReadContentAsJSON(self):
                cnt = self.ReadContent()
                if cnt:
                    if not 'json' in globals():
                        import json
                    try:
                        return json.loads(cnt)
                    except:
                        raise Exception('Error to parse JSON response : %s' % cnt)
                return None
    
            # ------------------------------------------------------------------------
    
            def WriteContentToFile(self, filepath, progressCallback=None):
                fSize = self._contentLength
                buf = MicroWebCli._tryAllocByteArray(
                    fSize if fSize and fSize < 10240 else 10240)
                if not buf:
                    raise MemoryError('Not enough memory to allocate buffer')
                buf = memoryview(buf)
                try:
                    file = open(filepath, 'wb')
                except:
                    raise Exception('Error to create file (%s)' % filepath)
                sizeRem = fSize
                pgrSize = 0
    
                #import uhashlib, binascii
                #sha = uhashlib.sha256()
    
                while sizeRem is None or sizeRem > 0:
                    if sizeRem and sizeRem < len(buf):
                        buf = buf[:sizeRem]
    
                    x = self.ReadContentInto(buf)
                    if x == 0:
                        break
                    if x < len(buf):
                        buf = buf[:x]
                    if sizeRem:
                        sizeRem -= x
                    #print('len(buf)', len(buf), 'buf', bytes(buf[:len(buf)-1]))
                    #tmp = bytes(buf[:len(buf)])
                    #print(tmp)
                    #sha.update(tmp)
                    try:
                        file.write(buf)
                    except Exception as ex:
                        print('file.write : %s' % ex)
                    pgrSize += x
                    if progressCallback:
                        try:
                            progressCallback(self, pgrSize, fSize)
                        except Exception as ex:
                            print('Error in progressCallback : %s' % ex)
                file.close()
                self.Close()
    
                #print(binascii.hexlify(sha.digest()))
    
                if sizeRem and sizeRem > 0:
                    if not 'remove' in globals():
                        from os import remove
                    remove(filepath)
                    raise Exception('Error to receive and save file (%s)' %
                                    filepath)
    
            # ------------------------------------------------------------------------
    
            def IsClosed(self):
                return self._microWebCli.IsClosed()
    
            # ------------------------------------------------------------------------
    
            def Close(self):
                self._microWebCli.Close()
    
        # ============================================================================
        # ============================================================================
        # ============================================================================
    
    import network, time, socket
    from machine import UART
    from fpioa_manager import fm, board_info
    
    if 'wlan' not in locals():
    
        # En EP8285 AT
    
        fm.register(27, fm.fpioa.UART2_TX, force=True)
        fm.register(28, fm.fpioa.UART2_RX, force=True)
    
        uart = UART(UART.UART2, 115200*8, 8, 2, 2, timeout=3000, read_buf_len=10240)
    
        time.sleep(3)
        wlan = network.ESP8285(uart)
    
        WIFI_SSID = "webduino.io"
        WIFI_PASW = "webduino"
    
        err = 0
        while 1:
            try:
                wlan.connect(WIFI_SSID, WIFI_PASW)
            except Exception:
                err += 1
                print("Connect AP failed, now try again")
                if err > 3:
                    raise Exception("Conenct AP fail")
                continue
            break
    
    print(wlan.ifconfig())
    print(wlan.isconnected())
    
    def progressCallback(microWebCli, progressSize, totalSize):
        if totalSize:
            pass #
            print('Progress: %d bytes of %d downloaded...' % (progressSize, totalSize))
        else:
            pass #
            print('Progress: %d bytes downloaded...' % progressSize)
    
    filename = '/sd/face_test.kmodel'
    fileurl = 'http://120.78.165.108/juwan/face.kmodel'
    
    try:
        import os
        os.remove(filename)
    except Exception as e:
        pass
    
    contentType = MicroWebCli.FileRequest(fileurl, filename, progressCallback)
    print('File of content type "%s" was saved to "%s"' % (contentType, filename))
    
    
  • 相关阅读:
    015.Python函数名的使用以及函数变量的操作
    014.Python函数
    013.Python的文件操作
    012.Python的字典和集合的相关函数
    git入门
    Visual Studio 常见的快捷键
    SVN使用
    C++ 一些特性
    C++ 引用、构造函数、移动语义
    WPF的AutoCompleteBox控件
  • 原文地址:https://www.cnblogs.com/juwan/p/12791366.html
Copyright © 2011-2022 走看看