Python之串口助手功能(3/4)

widget_ymodem.py
代码实现串口的Ymodem收发和进度条显示

# -*- coding: utf-8 -*-

import logging
import os

from data_check import crc16_ccitt

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# ymodem data header byte
SOH = b'\x01'
STX = b'\x02'
EOT = b'\x04'
ACK = b'\x06'
NAK = b'\x15'
CAN = b'\x18'
CRC = b'C'


class YMODEM(object):

    # initialize
    def __init__(self, getc, putc, type, mode='ymodem', header_pad=b'\x00', pad=b'\x1a'):
        self.getc = getc
        self.putc = putc
        self.type = type
        self.mode = mode
        self.header_pad = header_pad
        self.pad = pad
        self.log = logging.getLogger('YReporter')

    # send abort(CAN) twice
    def abort(self, count=2):
        for _ in range(count):
            self.putc(CAN)

    '''
    send entry
    '''

    def send(self, file_stream, file_name, file_size=0, retry=20, timeout=15, callback=None):
        try:
            packet_size = dict(
                ymodem=1024,
                ymodem128=128
            )[self.mode]
        except KeyError:
            raise ValueError("Invalid mode specified: {self.mode!r}".format(self=self))

        self.log.debug('Begin start sequence')

        # Receive first character
        error_count = 0
        cancel = 0
        sent = 0

        def processResult(state):
            if callable(callback):
                callback(file_size, sent, state)

        while True:
            char = self.getc(1)
            # print('Ymodem_Send filename', char)
            if char:
                if char == CRC:
                    # Expected CRC
                    self.log.info("<<< CRC")
                    break
                elif char == CAN:
                    self.log.info("<<< CAN")
                    if cancel:
                        processResult(-1)
                        return False
                    else:
                        cancel = 1
                else:
                    self.log.error("send error, expected CRC or CAN, but got " + hex(ord(char)))

            error_count += 1
            if error_count > retry:
                processResult(-1)
                self.abort()
                self.log.error("send error: error_count reached %d aborting", retry)
                return False

        header = self._make_send_header(128, 0)
        name = bytes(file_name, encoding="utf8")

        size = bytes(str(file_size), encoding="utf8")

        data = name + b'\x00' + size + b'\x20'

        data = data.ljust(128, self.header_pad)

        checksum = self._make_send_checksum(data)
        data_for_send = header + data + checksum
        self.putc(data_for_send)
        # self.log.info("Packet 0 >>> " + str(len(data_for_send)))

        error_count = 0
        cancel = 0
        # Receive reponse of first packet

        if self.type == 'I2C':
            while True:
                char = self.getc(1)
                if char:
                    if char == ACK:
                        self.log.info("<<< ACK")
                        char2 = self.getc(1)
                        if char2 == CRC:
                            self.log.info("<<< CRC")
                            break
                        else:
                            self.log.warn("ACK wasnt CRCd")
                            break
                    elif char == CAN:
                        self.log.info("<<< CAN")
                        if cancel:
                            processResult(-1)
                            return False
                        else:
                            cancel = 1
                    else:
                        if (ord(char)) >= 0x20 and (ord(char)) <= 0x7e:
                            self.log.error("test" + str(char))
                        else:
                            self.log.error("send error, expected ACK or CAN, but got " + hex(ord(char)))
        else:
            while True:
                char = self.getc(2)
                # print('Ymodem_Send filecontent', char)
                if char:
                    if char == b'\x06C':
                        self.log.info("<<< ACK")
                        break
                    elif char == CAN:
                        self.log.info("<<< CAN")
                        if cancel:
                            processResult(-1)
                            return False
                        else:
                            cancel = 1
                    else:
                        if (error_count > 20):
                            processResult(-1)
                            return False
                        error_count += 1

                        if (ord(char)) >= 0x20 and (ord(char)) <= 0x7e:
                            self.log.error("test" + str(char))
                        else:
                            self.log.error("send error, expected ACK or CAN, but got " + hex(ord(char)))

        error_count = 0
        cancel = 0
        total_packets = 1
        sequence = 1
        while True:
            # Read raw data from file stream
            data = file_stream.read(packet_size)
            if not data:
                self.log.debug('send: at EOF')
                break
            total_packets += 1
            sent += len(data)

            header = self._make_send_header(packet_size, sequence)
            data = data.ljust(packet_size, self.pad)
            checksum = self._make_send_checksum(data)

            while True:
                data_for_send = header + data + checksum
                # data_in_hexstring = "".join("%02x" % b for b in data_for_send)

                # Send file data packet
                self.putc(data_for_send)
                # self.log.info("Packet " + str(sequence) + " >>>" + str(len(data_for_send)))
                error_count = 0
                while True:
                    char = self.getc(1)
                    if char == ACK:
                        break
                    else:
                        error_count += 1
                    if error_count > retry:
                        processResult(-1)
                        self.abort()
                        return False

                error_count = 0
                if char == ACK:
                    # Expected response
                    # self.log.info("<<< ACK")
                    processResult(0)
                    error_count = 0
                    break

                error_count += 1

                if error_count > retry:
                    processResult(-1)
                    self.abort()
                    self.log.error('send error: NAK received %d , aborting', retry)
                    return False

            sequence = (sequence + 1) % 0x100

        # Send EOT and expect final ACK
        error_count = 0
        while True:
            self.putc(EOT)
            self.log.info(">>> EOT")
            char = self.getc(1)
            if char == ACK:
                self.log.info("<<< ACK")
                break
            else:
                error_count += 1
                if error_count > retry:
                    processResult(-1)
                    self.abort()
                    self.log.warn('EOT wasnt ACKd, aborting transfer')
                    return False

        header = self._make_send_header(128, 0)

        data = bytearray(b'\x00')

        data = data.ljust(128, self.header_pad)

        checksum = self._make_send_checksum(data)
        data_for_send = header + data + checksum
        self.putc(data_for_send)

        error_count = 0
        while True:
            char = self.getc(1)
            if char == ACK:
                break
            else:
                error_count += 1
                if error_count > retry:
                    processResult(-1)
                    self.abort()
                    self.log.warn('SOH wasnt ACK, aborting transfer')
                    return False

        self.log.info('Transmission successful (ACK received)')
        processResult(1)
        return True

    def sendFile(self, filename, callback=None):
        if not os.path.exists(filename):
            if callable(callback):
                callback(1, 0, -1)
                return False
        sz = os.path.getsize(filename)
        if sz <= 0:
            if callable(callback):
                callback(1, 0, -1)
                return False

        f = open(filename, 'rb')
        res = self.send(f, os.path.basename(filename), sz, callback=callback)
        f.close()
        return res

    # Header byte
    def _make_send_header(self, packet_size, sequence):
        assert packet_size in (128, 1024), packet_size
        _bytes = []
        if packet_size == 128:
            _bytes.append(ord(SOH))
        elif packet_size == 1024:
            _bytes.append(ord(STX))
        _bytes.extend([sequence, 0xff - sequence])
        return bytearray(_bytes)

    # Make check code
    def _make_send_checksum(self, data):
        _bytes = []
        crc = crc16_ccitt(data)
        _bytes.extend([crc >> 8, crc & 0xff])
        return bytearray(_bytes)

    def _verify_recv_checksum(self, data):
        _checksum = bytearray(data[-2:])
        their_sum = (_checksum[0] << 8) + _checksum[1]
        data = data[:-2]

        our_sum = crc16_ccitt(data)
        valid = bool(their_sum == our_sum)
        return valid, data


if __name__ == '__main__':
    pass

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值