def _recv_bin32_header(self, timeout): ''' Receive a header with 32 bit CRC. ''' header = [] mine = 0 for x in xrange(0, 5): char = self._recv(timeout) if char is TIMEOUT: return 0, False else: mine = self.calc_crc32(chr(char), mine) header.append(char) # Read their crc rcrc = self._recv(timeout) rcrc |= self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) << 0x10 rcrc |= self._recv(timeout) << 0x18 log.debug('My CRC = %08x, theirs = %08x' % (mine, rcrc)) if mine != rcrc: log.error('Invalid CRC32 in header') return 0, False else: return 5, header
def _recv_32_data(self, timeout): char = 0 data = [] mine = 0 while char < 0x100: char = self._recv(timeout) if char is TIMEOUT: return TIMEOUT, '' elif char < 0x100: mine = self.calc_crc32(chr(char & 0xff), mine) data.append(chr(char)) # Calculate our crc, unescape the sub_frame_kind sub_frame_kind = char ^ ZDLEESC mine = self.calc_crc32(chr(sub_frame_kind), mine) # Read their crc rcrc = self._recv(timeout) rcrc |= self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) << 0x10 rcrc |= self._recv(timeout) << 0x18 log.debug('My CRC = %08x, theirs = %08x' % (mine, rcrc)) if mine != rcrc: log.error('Invalid CRC32') return timeout, '' else: return sub_frame_kind, ''.join(data)
def _recv_hex_header(self, timeout): ''' Receive a header with HEX encoding. ''' header = [] mine = 0 for x in xrange(0, 5): char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT mine = self.calc_crc16(chr(char), mine) header.append(char) # Read their crc char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT rcrc = char << 0x08 char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT rcrc |= char log.debug('My CRC = %04x, theirs = %04x' % (mine, rcrc)) if mine != rcrc: log.error('Invalid CRC16 in receiving HEX header') return 0, False # Read to see if we receive a carriage return char = self.getc(1, timeout) if char == '\r': # Expect a second one (which we discard) self.getc(1, timeout) return 5, header
def _recv_16_data(self, timeout): char = 0 data = [] mine = 0 while char < 0x100: char = self._recv(timeout) if char is INVDATA: return INVDATA, b'' elif char is TIMEOUT: log.error('Invalid b16 data timeout: %d' % len(data)) return TIMEOUT, b'' elif char is ZCAN: return ZCAN, b'' elif char < 0x100: mine = self.calc_crc16(char & 0xff, mine) #log.info("%02x"%char) data.append(bytes([char])) # Calculate our crc, unescape the sub_frame_kind sub_frame_kind = char ^ ZDLEESC mine = self.calc_crc16(sub_frame_kind, mine) #log.info("ch:0x%02x,crc:0x%08x"%(sub_frame_kind,mine)) # Read their crc rcrc = self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) if mine != rcrc: log.error('Invalid b16 data My CRC = %08x, theirs = %08x, len:%d' % (mine, rcrc, len(data))) return INVDATA, b'' else: return sub_frame_kind, b''.join(data)
def _recv_bin32_header(self, timeout): ''' Receive a header with 32 bit CRC. ''' header = [] mine = 0 for x in range(0, 5): char = self._recv(timeout) if char is TIMEOUT: return 0, header else: mine = self.calc_crc32(bytes([char]), mine) header.append(char) # Read their crc rcrc = self._recv(timeout) rcrc |= self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) << 0x10 rcrc |= self._recv(timeout) << 0x18 if mine != rcrc: log.error('Invalid b32 header CRC = %08x, theirs = %08x' % (mine, rcrc)) return 0, header else: return 5, header
def _recv_32_data(self, timeout): char = 0 data = [] mine = 0 while char < 0x100: char = self._recv(timeout) if char is TIMEOUT: return TIMEOUT, '' elif char < 0x100: mine = self.calc_crc32(bytes([char & 0xff]), mine) data.append(bytes([char])) # Calculate our crc, unescape the sub_frame_kind sub_frame_kind = char ^ ZDLEESC mine = self.calc_crc32(bytes([sub_frame_kind]), mine) # Read their crc rcrc = self._recv(timeout) rcrc |= self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) << 0x10 rcrc |= self._recv(timeout) << 0x18 if mine != rcrc: log.error('Invalid b32 data My CRC = %08x, theirs = %08x len:%d' % (mine, rcrc, len(data))) return timeout, b'' else: return sub_frame_kind, b''.join(data)
def _wait_recv(self, error_count, retry, timeout): ''' Waits for a <NAK> or <CRC> before starting the transmission. Return <NAK> or <CRC> on success, ``False`` in case of failure ''' # Initialize protocol cancel = 0 # Loop until the first character is a control character (NAK, CRC) or # we reach the retry limit while True: char = self.getc(1) print char ; if char: if char in [NAK, CRC]: print "char is NAK or CRC" return char elif char == CAN: # Cancel at two consecutive cancels if cancel: log.error(error.ABORT_RECV_CAN_CAN) self.abort(timeout=timeout) return False else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 else: # Ignore the rest pass error_count += 1 if error_count >= retry: self.abort(timeout=timeout) return False
def _wait_recv(self, error_count, timeout): ''' Waits for a <NAK> or <CRC> before starting the transmission. Return <NAK> or <CRC> on success, ``False`` in case of failure ''' # Initialize protocol cancel = 0 # Loop until the first character is a control character (NAK, CRC) or # we reach the retry limit while True: char = self.getc(1) if char: if char in [NAK, CRC]: return char elif char == CAN: # Cancel at two consecutive cancels if cancel: log.error(error.ABORT_RECV_CAN_CAN) self.abort(timeout=timeout) return False else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 else: # Ignore the rest pass error_count += 1 if error_count >= retry: self.abort(timeout=timeout) return False
def _send_stream(self, stream, crc_mode, retry=16, timeout=0): ''' Sends a stream according to the given protocol dialect: >>> stream = file('/etc/issue', 'rb') >>> print modem.send(stream) True Return ``True`` on success, ``False`` in case of failure. ''' # Get packet size for current protocol packet_size = PACKET_SIZE.get(self.protocol, 128) # ASSUME THAT I'VE ALREADY RECEIVED THE INITIAL <CRC> OR <NAK> # SO START DIRECTLY WITH STREAM TRANSMISSION sequence = 1 error_count = 0 while True: data = stream.read(packet_size) # Check if we're done sending if not data: break # Select optimal packet size when using YMODEM if self.protocol == PROTOCOL_YMODEM: packet_size = (len(data) <= 128) and 128 or 1024 # Align the packet data = data.ljust(packet_size, b'\x00') # Calculate CRC or checksum crc = crc_mode and self.calc_crc16(data) or \ self.calc_checksum(data) # SENDS PACKET WITH CRC if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): log.error(error.ERROR_SEND_PACKET) return False # Next sequence sequence = (sequence + 1) % 0x100 # STREAM FINISHED, SEND EOT log.debug(error.DEBUG_SEND_EOT) if self._send_eot(error_count, retry, timeout): return True else: log.error(error.ERROR_SEND_EOT) return False
def _send_stream(self, stream, crc_mode, retry=16, timeout=0): ''' Sends a stream according to the given protocol dialect: >>> stream = file('/etc/issue', 'rb') >>> print modem.send(stream) True Return ``True`` on success, ``False`` in case of failure. ''' # Get packet size for current protocol packet_size = PACKET_SIZE.get(self.protocol, 128) # ASSUME THAT I'VE ALREADY RECEIVED THE INITIAL <CRC> OR <NAK> # SO START DIRECTLY WITH STREAM TRANSMISSION sequence = 1 error_count = 0 while True: data = stream.read(packet_size) # Check if we're done sending if not data: break # Select optimal packet size when using YMODEM if self.protocol == PROTOCOL_YMODEM: packet_size = (len(data) <= 128) and 128 or 1024 # Align the packet data = data.ljust(packet_size, '\x00') # Calculate CRC or checksum crc = crc_mode and self.calc_crc16(data) or \ self.calc_checksum(data) # SENDS PACKET WITH CRC if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): log.error(error.ERROR_SEND_PACKET) return False # Next sequence sequence = (sequence + 1) % 0x100 # STREAM FINISHED, SEND EOT log.debug(error.DEBUG_SEND_EOT) if self._send_eot(error_count, retry, timeout): return True else: log.error(error.ERROR_SEND_EOT) return False
def _send_packet(self, sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): ''' Sends one single packet of data, appending the checksum/CRC. It retries in case of errors and wait for the <ACK>. Return ``True`` on success, ``False`` in case of failure. ''' start_char = SOH if packet_size == 128 else STX while True: self.putc(start_char) self.putc(sequence.to_bytes(1, 'big')) self.putc((0xff - sequence).to_bytes(1, 'big')) self.putc(data) if crc_mode: self.putc((crc >> 8).to_bytes(1, 'big')) self.putc((crc & 0xff).to_bytes(1, 'big')) else: # Send CRC or checksum self.putc((crc).to_bytes(1, 'big')) # Wait for the <ACK> char = self.getc(1, timeout) if char == ACK: # Transmission of the character was successful return True if char in [None, NAK]: error_count += 1 if error_count >= retry: # Excessive amounts of retransmissions requested self.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return False continue # Protocol error log.error(error.ERROR_PROTOCOL) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return False
def _send_packet(self, sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): ''' Sends one single packet of data, appending the checksum/CRC. It retries in case of errors and wait for the <ACK>. Return ``True`` on success, ``False`` in case of failure. ''' start_char = SOH if packet_size == 128 else STX while True: self.putc(start_char) self.putc(chr(sequence)) self.putc(chr(0xff - sequence)) self.putc(data) if crc_mode: self.putc(chr(crc >> 8)) self.putc(chr(crc & 0xff)) else: # Send CRC or checksum self.putc(chr(crc)) # Wait for the <ACK> char = self.getc(1, timeout) if char == ACK: # Transmission of the character was successful return True if char in [None, NAK]: error_count += 1 if error_count >= retry: # Excessive amounts of retransmissions requested self.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return False continue # Protocol error log.error(error.ERROR_PROTOCOL) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return False
def send(self, stream, retry=16, timeout=60, quiet=0): ''' Send a stream via the XMODEM protocol. >>> stream = file('/etc/issue', 'rb') >>> print modem.send(stream) True Returns ``True`` upon succesful transmission or ``False`` in case of failure. ''' # initialize protocol error_count = 0 crc_mode = 0 cancel = 0 while True: char = self.getc(1) if char: if char == NAK: crc_mode = 0 break elif char == CRC: crc_mode = 1 break elif char == CAN: # We abort if we receive two consecutive <CAN> bytes if cancel: return False else: cancel = 1 else: log.error(error.ERROR_EXPECT_NAK_CRC % ord(char)) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return False # Start sending the stream return self._send_stream(stream, crc_mode, retry, timeout)
def _send_eot(self, error_count, retry, timeout): ''' Sends an <EOT> code. It retries in case of errors and wait for the <ACK>. Return ``True`` on success, ``False`` in case of failure. ''' while True: self.putc(EOT) # Wait for <ACK> char = self.getc(1, timeout) if char == ACK: # <EOT> confirmed return True else: error_count += 1 if error_count >= retry: # Excessive amounts of retransmissions requested, # abort transfer log.error(error.ABORT_ERROR_LIMIT) return False
def _recv_bin16_header(self, timeout): ''' Recieve a header with 16 bit CRC. ''' header = [] mine = 0 for x in xrange(0, 5): char = self._recv(timeout) if char is TIMEOUT: return 0, False else: mine = self.calc_crc16(chr(char), mine) header.append(char) rcrc = self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) if mine != rcrc: log.error('Invalid CRC16 in header') return 0, False else: return 5, header
def _recv_bin16_header(self, timeout): ''' Recieve a header with 16 bit CRC. ''' header = [] mine = 0 for x in range(0, 5): char = self._recv(timeout) if char is TIMEOUT: return 0, header else: mine = self.calc_crc16(char, mine) header.append(char) rcrc = self._recv(timeout) << 0x08 rcrc |= self._recv(timeout) if mine != rcrc: log.error('Invalid b16 header CRC = %04x, theirs = %04x' % (mine, rcrc)) return 0, header else: return 5, header
def _recv_hex_header(self, timeout): ''' Receive a header with HEX encoding. ''' header = [] mine = 0 for x in range(0, 5): char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT, header mine = self.calc_crc16(char, mine) header.append(char) # Read their crc char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT, header rcrc = char << 0x08 char = self._recv_hex(timeout) if char is TIMEOUT: return TIMEOUT, header rcrc |= char if mine != rcrc: log.error('Invalid hex header My CRC = %04x, theirs = %04x' % (mine, rcrc)) return 0, header # Read to see if we receive a carriage return char = self._recv_raw(timeout) if (char & 0x7F) == ord(b'\r'): # Expect a second one (which we discard) self._recv_raw(timeout) else: print('hex end:0x%2x' % char) return 5, header
def send(self, stream, retry=16, timeout=60): ''' Send a stream via the XMODEM1K protocol. >>> stream = file('/etc/issue', 'rb') >>> print modem.send(stream) True Returns ``True`` upon succesful transmission or ``False`` in case of failure. ''' # initialize protocol error_count = 0 crc_mode = 0 cancel = 0 while True: char = self.getc(1) if char: if char == NAK: crc_mode = 0 break elif char == CRC: crc_mode = 1 break elif char == CAN: if cancel: log.debug(error.DEBUG_RECV_CAN) return False else: log.error(error.ABORT_RECV_CAN_CAN) cancel = 1 else: log.error(error.ERROR_EXPECT_NAK_CRC % ord(char)) error_count += 1 if error_count >= retry: self.abort(timeout=timeout) return False if self._send_stream(stream, crc_mode, retry, timeout): return True else: log.error(error.ABORT_SEND_STREAM) return False
def _send_packet(self, sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): print('sequence', sequence) ''' Sends one single packet of data, appending the checksum/CRC. It retries in case of errors and wait for the <ACK>. Return ``True`` on success, ``False`` in case of failure. ''' start_char = SOH if packet_size == 128 else STX while True: self.putc(start_char) self.putc(bytes([sequence])) self.putc(bytes([0xff - sequence])) self.putc(data) if crc_mode: self.putc(bytes([crc >> 8])) self.putc(bytes([crc & 0xff])) else: # Send CRC or checksum self.putc(bytes([crc])) # Wait for the <ACK> char = self.getc(1, 0.1) # TODO: Due to the delay of the serial, there may be a miss of ACK, # so the timeout would send back CRC. We force it to ACK manually. char = ACK if char == ACK: # Transmission of the character was successful return True if char in [None, NAK]: error_count += 1 if error_count >= retry: # Excessive amounts of retransmissions requested log.error(error.ABORT_ERROR_LIMIT) return False continue # Protocol error log.error(error.ERROR_PROTOCOL) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) return False
def _recv_file(self, basedir, timeout, retry): log.info('About to receive a file in %s' % (basedir, )) pos = 0 # Read the data subpacket containing the file information kind, data = self._recv_data(pos, timeout) pos += len(data) if kind not in [FRAMEOK, ENDOFFRAME]: if not kind is TIMEOUT: # File info metadata corrupted self._send_znak(pos, timeout) return False # We got the file name part = data.split(b'\x00') filename = part[0] if not isinstance(basedir, bytes): basedir = bytes(basedir, "utf-8") filepath = os.path.join(basedir, os.path.basename(filename)) fp = open(filepath, 'w+b') if not fp: log.error("open file [%s] err" % filepath) return False part = part[1].replace(b' ', b' ').split(b' ') log.info('Meta %r' % (part, )) size = int(part[0]) # Date is octal (!?) date = datetime.datetime.fromtimestamp(int( part[1])) if len(part) > 1 and part[1] else b'' # We ignore mode and serial number, whatever, dude :-) log.info('kind:%d Receiving file "%s" with size %d, mtime %s' % \ (kind, filename, size, date)) # Receive contents start = time.time() kind = ZFILE total_size = 0 continues_no_file_data_received_cnt = 0 while continues_no_file_data_received_cnt < retry: kind, chunk_size = self._recv_file_data(kind, fp.tell(), fp, timeout) print('size %08d/%08d, %d' % (total_size, size, chunk_size)) if chunk_size <= 0: continues_no_file_data_received_cnt += 1 else: total_size += chunk_size continues_no_file_data_received_cnt = 0 if size > 0 and total_size >= size and continues_no_file_data_received_cnt > 3: break if kind == ZEOF: break # End of file speed = (total_size / (time.time() - start)) if kind == ZEOF: log.info('Receiving file "%s" done at %.02f bps' % (filename, speed)) else: log.info('Receiving file "%s" Error at %.02f bps' % (filename, speed)) # Update file metadata fp.close() #mtime = time.mktime(date.timetuple()) #TODO JJJ #os.utime(filepath, (mtime, mtime)) #TODO JJJ return True
def send(self, filenames, retry=100, timeout=60): ''' Send one or more files via the YMODEM protocol. >>> print modem.send('*.txt') True Returns ``True`` upon succesful transmission or ``False`` in case of failure. ''' print "start sending..." # Get a list of files to send #filenames = glob.glob(pattern) if not filenames: return True print "after globbing.." # initialize protocol error_count = 0 crc_mode = 0 start_char = self._wait_recv(error_count, retry, timeout) if start_char: crc_mode = 1 if (start_char == CRC) else 0 else: log.error(error.ABORT_PROTOCOL) # Already aborted return False print "StartChar received.." for filename in filenames: print "start filename: ", filename # Send meta data packet sequence = 0 error_count = 0 # REQUIREMENT 1,1a,1b,1c,1d print "s 1" data = ''.join([os.path.basename(filename), '\x00']) print "s 2" #log.debug(error.DEBUG_START_FILE % (filename,)) print "s 3" # Pick a suitable packet length for the filename packet_size = 128 if (len(data) < 128) else 1024 print "send filename" # Packet padding data = data.ljust(packet_size, '\0') print "s 4" # Calculate checksum crc = self.calc_crc16(data) if crc_mode else self.calc_checksum(data) print "s 5" # Emit packet if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): self.abort(timeout=timeout) return False print "wating for CRC" # Wait for <CRC> before transmitting the file contents error_count = 0 if not self._wait_recv(error_count, timeout): self.abort(timeout) return False filedesc = open(filename, 'rb') # AT THIS POINT # - PACKET 0 WITH METADATA TRANSMITTED # - INITIAL <CRC> OR <NAK> ALREADY RECEIVED print "wating for CRC" if not self._send_stream(filedesc, crc_mode, retry, timeout): #log.error(error.ABORT_SEND_STREAM) return False # AT THIS POINT # - FILE CONTENTS TRANSMITTED # - <EOT> TRANSMITTED # - <ACK> RECEIVED print "stream send" filedesc.close() # WAIT A <CRC> BEFORE NEXT FILE error_count = 0 if not self._wait_recv(error_count, retry, timeout): log.error(error.ABORT_INIT_NEXT) # Already aborted return False # End of batch transmission, send NULL file name sequence = 0 error_count = 0 packet_size = 128 data = '\x00' * packet_size crc = self.calc_crc16(data) if crc_mode else self.calc_checksum(data) # Emit packet if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): log.error(error.ABORT_SEND_PACKET) # Already aborted return False # All went fine return True
def recv(self, basedir, crc_mode=1, retry=16, timeout=60, delay=1): ''' Receive some files via the YMODEM protocol and place them under ``basedir``:: >>> print modem.recv(basedir) 3 Returns the number of files received on success or ``None`` in case of failure. N.B.: currently there are no control on the existence of files, so they will be silently overwritten. ''' # Initiate protocol error_count = 0 char = 0 cancel = 0 sequence = 0 num_files = 0 while True: # First try CRC mode, if this fails, fall back to checksum mode if error_count >= retry: self.abort(timeout=timeout) return None elif crc_mode and error_count < (retry / 2): if not self.putc(CRC): time.sleep(delay) error_count += 1 else: crc_mode = 0 if not self.putc(NAK): time.sleep(delay) error_count += 1 # <CRC> or <NAK> sent, waiting answer char = self.getc(1, timeout) if char is None: error_count += 1 continue elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 continue elif char in [SOH, STX]: break else: error_count += 1 continue # Receiver loop fileout = None while True: # Read next file in batch mode while True: if char is None: error_count += 1 elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(debug.DEBUG_RECV_CAN) cancel = 1 continue elif char in [SOH, STX]: seq1 = ord(self.getc(1)) seq2 = 0xff - ord(self.getc(1)) if seq1 == sequence and seq2 == sequence: packet_size = 128 if char == SOH else 1024 data = self.getc(packet_size + 1 + crc_mode) data = self._check_crc(data, crc_mode) if data: filename = data.split('\x00')[0] if not filename: # No filename, end of batch reception self.putc(ACK) return num_files log.info('Receiving %s to %s' % (filename, basedir)) fileout = open( os.path.join(basedir, os.path.basename(filename)), 'wb') if not fileout: log.error(error.ABORT_OPEN_FILE) self.putc(NAK) self.abort(timeout=timeout) return False else: self.putc(ACK) break # Request retransmission if something went wrong self.getc(packet_size + 1 + crc_mode) self.putc(NAK) self.getc(1, timeout) continue else: error_count += 1 self.getc(packet_size + 1 + crc_mode) self.putc(NAK) self.getc(1, timeout) stream_size = self._recv_stream(fileout, crc_mode, retry, timeout, delay) if not stream_size: log.error(error.ABORT_RECV_STREAM) return False log.debug('File transfer done, requesting next') fileout.close() num_files += 1 sequence = 0 # Ask for the next sequence and receive the reply self.putc(CRC) char = self.getc(1, timeout)
def send(self, pattern, retry=16, timeout=60, info_callback=None): # Get a list of files to send file_info_list = [] try: if os.path.isdir(pattern): for dirpath, dirnames, topfilenames in os.walk(pattern): file_info_list.extend([(os.path.join(dirpath, n), os.stat(os.path.join(dirpath, n))) for n in topfilenames]) for subdir in dirnames: subdirpath, subdirnames, subfilenames = os.walk( os.path.join(dirpath, subdir)) file_info_list.extend([ (os.path.join(dirpath, n), os.stat(os.path.join(dirpath, n))) for n in subfilenames ]) elif os.path.isfile(pattern): file_info_list.append((pattern, os.stat(pattern))) except Exception as e: log.error("zmodem get file err:%s" % e) if not file_info_list: return True DATA_SIZE_100K = 1024 * 100 DATA_SIZE_1M = 1014 * 1024 DATA_SIZE_10M = 1014 * 1024 * 10 SUB_PACKERT_SIZE = 1024 MIDDLE_SUB_PACKET_SIZE = 512 MIN_SUB_PACKET_SIZE = 256 SUB_PACKERT_ACK_PER = 16 SUB_PACKERT_ACK_PER_MIN = 4 SUB_PACKERT_ACK_PER_MAX = 128 # initialize protocol error_count = 0 crc_mode = 0 zr_init_flags = 0 zr_init_buffer_size = 0 total_file_cnt = len(file_info_list) last_state = cur_state = self.SENDER_WAIT_INTI self._send_wakeup(timeout) self._send_zsqinit(timeout) for file_index, file_info in enumerate(file_info_list): if cur_state == self.SEND_ALL_DONE_ABORT: print("receiver abort") break filename = file_info[0] filestat = file_info[1] fd = open(filename, "rb") if not fd: print("open %s fail,skip" % filename) continue file_stat = os.fstat(fd.fileno()) left_all_file_size = self.get_left_file_size( file_info_list, file_index) last_send_pos = send_pos = recv_ack_pos = recv_zpos = 0 send_data_frame_idx = 0 left_files_to_tran = total_file_cnt - file_index data_frame_packet_size = SUB_PACKERT_SIZE if file_stat.st_size < DATA_SIZE_100K: send_data_frame_cnt = SUB_PACKERT_ACK_PER_MIN elif file_stat.st_size > DATA_SIZE_10M: send_data_frame_cnt = SUB_PACKERT_ACK_PER_MAX elif file_stat.st_size > DATA_SIZE_1M: #1M ~ 10M send_data_frame_cnt = SUB_PACKERT_ACK_PER + ( (file_stat.st_size - DATA_SIZE_1M) / DATA_SIZE_100K) * ( SUB_PACKERT_ACK_PER_MAX - SUB_PACKERT_ACK_PER) / ( (DATA_SIZE_10M - DATA_SIZE_1M) / DATA_SIZE_100K) else: # 100K ~ 1M send_data_frame_cnt = SUB_PACKERT_ACK_PER_MIN + ( (file_stat.st_size - DATA_SIZE_100K) // DATA_SIZE_100K) * ( SUB_PACKERT_ACK_PER - SUB_PACKERT_ACK_PER_MIN) / ( (DATA_SIZE_1M - DATA_SIZE_100K) / DATA_SIZE_100K) send_data_frame_cnt = int(send_data_frame_cnt) debug_info = 'filename:%s, size:%d, frmcnt:%d\n' % ( filename, file_stat.st_size, send_data_frame_cnt) if info_callback: info_callback(debug_info) else: print(debug_info) if file_index > 0 and (cur_state == self.SENDING_ONE_DONE_AND_HAS_NEXT_FILE or cur_state == self.SENDING_SKIP): last_state = cur_state = self.SENDING_ZFILE_NEXT else: last_state = cur_state = self.SENDER_WAIT_INTI continues_zpos_cnt = 0 continues_start_zpos = 0 end_of_file_send = False send_zdata_frame_type = 0 send_data_err_cnt = 0 error_count = 0 file_data_need_async_ack = 0 data_need_ack_time = 0 percent_last = percent_now = 0 while True: percent_last = percent_now last_state = cur_state last_send_pos = send_pos recv_zpos_get = False header_need_ack = file_data_need_sync_ack = False if last_state == self.SENDING_ZDATA: if send_zdata_frame_type == ZCRCQ: file_data_need_async_ack += 1 data_need_ack_time = time.time() elif send_zdata_frame_type == ZCRCW: file_data_need_sync_ack = True data_need_ack_time = time.time() elif last_state != self.SENDING_ZFILE_NEXT: header_need_ack = True if header_need_ack or file_data_need_sync_ack: recv_timeout = 0.3 + file_data_need_async_ack * 0.1 + error_count * 0.03 elif file_data_need_async_ack > 1: recv_timeout = file_data_need_async_ack * 0.03 else: recv_timeout = 0.0 header = self._recv_header(recv_timeout) #print('h:%s, sync:%s async:%d, er:%d ->%f'%(header_need_ack, file_data_need_sync_ack, file_data_need_async_ack, error_count, recv_timeout)) header_type = header[0] if header_type == ZRPOS: recv_zpos_get = True recv_zpos = header[ZP0] | (header[ZP1] << 8) | ( header[ZP2] << 16) | (header[ZP3] << 24) elif header_type == ZACK: recv_ack_pos = header[ZP0] | (header[ZP1] << 8) | ( header[ZP2] << 16) | (header[ZP3] << 24) if not file_data_need_sync_ack and file_data_need_async_ack > 0: file_data_need_async_ack -= 1 elif header_type == ZRINIT: zr_init_flags = header[ZF0] | (header[ZF1] << 8) zr_init_buffer_size = header[ZP0] | (header[ZP1] << 8) print(header, '0x%x' % zr_init_flags, zr_init_buffer_size) elif header_type != TIMEOUT: print('header_type:0x%x' % header_type if header_type is not None else "None") last_state, cur_state = self._send_state_next( last_state, header, end_of_file_send, left_files_to_tran) percent_now = 1000 * send_pos // (file_stat.st_size if file_stat.st_size > 0 else 1) if header_need_ack or file_data_need_sync_ack or file_data_need_async_ack or header_type != TIMEOUT or cur_state != last_state or percent_now != percent_last: process_info = '%3d.%d%% send:%-6d ack:%-6d zrpos:%d%s \n' % ( percent_now // 10, percent_now % 10, send_pos, recv_ack_pos, recv_zpos, '<N>' if recv_zpos_get else '') debug_info = "0x%2x frm:%4d/%d %s" % ( cur_state, send_data_frame_idx, send_data_frame_cnt, process_info) if info_callback: info_callback(debug_info) else: print(debug_info) if cur_state == last_state: if cur_state == self.SENDING_ZDATA: if file_data_need_sync_ack is True: if header_type == TIMEOUT: error_count += 1 elif error_count > 2: error_count -= 2 elif file_data_need_async_ack > 3 and header_type == TIMEOUT: error_count += 1 elif header_type == TIMEOUT: error_count += 1 if error_count > retry or send_data_err_cnt > retry: self._send_cancel_transfer() if info_callback: info_callback("too many errors, abort\n") break elif error_count > 0: error_count -= 1 if recv_zpos_get is True and recv_zpos < send_pos: send_pos = recv_zpos send_data_frame_idx = 0 continues_zpos_cnt += 1 if continues_zpos_cnt == 1: continues_start_zpos = recv_zpos if continues_zpos_cnt > 2 and send_data_frame_cnt >= 2: send_data_frame_cnt -= 1 elif continues_zpos_cnt > (retry + retry + -1) / 3: data_frame_packet_size = MIDDLE_SUB_PACKET_SIZE elif continues_zpos_cnt > (retry + retry + -1) / 2: data_frame_packet_size = MIN_SUB_PACKET_SIZE elif continues_zpos_cnt > retry and continues_start_zpos == recv_zpos: self._send_cancel_transfer() if info_callback: info_callback("too many errors, abort\n") break else: continues_zpos_cnt = 0 if cur_state == self.SENDING_ZRQINIT: if error_count > 1 and error_count % 2 == 0: self._send_wakeup(timeout) self._send_zsqinit(timeout) elif cur_state == self.SENDING_ZFILE: if self._send_zfile_header( filename, file_stat.st_size, file_stat.st_mtime, left_files_to_tran, left_all_file_size, timeout) < 0: send_data_err_cnt += 1 else: send_data_err_cnt = 0 elif cur_state == self.SENDING_ZDATA: if last_send_pos != send_pos: fd.seek(send_pos) data = fd.read(data_frame_packet_size) data_len = len(data) if send_data_frame_idx == 0 and data_len > 0: if send_zdata_frame_type == ZCRCG or send_zdata_frame_type == ZCRCQ: fd.seek(last_send_pos) fill_end_pack_data = fd.read(4) if self._write_zdle_data(ZCRCW, fill_end_pack_data, timeout) < 0: send_data_err_cnt += 1 else: send_data_err_cnt = 0 fd.seek(send_pos + data_len) if self._send_zdata_header(send_pos, timeout) < 0: send_data_err_cnt += 1 else: send_data_err_cnt = 0 send_data_frame_idx += 1 send_pos += data_len if data_len == 0: #file ends end_of_file_send = True self._send_zeof_header(send_pos, timeout) send_zdata_frame_type = ZEOF send_data_frame_idx = 0 elif (send_data_frame_idx % send_data_frame_cnt) == 0: if zr_init_buffer_size > 0 and zr_init_buffer_size <= send_data_frame_idx * data_frame_packet_size: send_zdata_frame_type = ZCRCW send_data_frame_idx = 0 elif zr_init_flags & CANFDX: send_zdata_frame_type = ZCRCQ else: send_zdata_frame_type = ZCRCW send_data_frame_idx = 0 if self._write_zdle_data( send_zdata_frame_type, data, timeout) < 0: #ZACK expected, end of frame send_data_err_cnt += 1 else: send_data_err_cnt = 0 elif data_len < data_frame_packet_size: #file end, and we recheck if really file end data_next = fd.read(data_frame_packet_size) send_zdata_frame_type = ZCRCE if not data_next else ZCRCW if self._write_zdle_data( send_zdata_frame_type, data, timeout ) < 0: # CRCE no ZACK expected, end of frame/file send_data_err_cnt += 1 else: send_data_err_cnt = 0 send_data_frame_idx = 0 #do not read the data, for next loop, and do not care of file offst for we will feek again else: if zr_init_buffer_size > 0 and zr_init_buffer_size <= send_data_frame_idx * data_frame_packet_size: send_zdata_frame_type = ZCRCW send_data_frame_idx = 0 elif zr_init_flags & CANOVIO: #atleast one ack every 3 seconds if time.time() > data_need_ack_time + 3.0: send_zdata_frame_type = ZCRCG else: send_zdata_frame_type = ZCRCG else: send_zdata_frame_type = ZCRCW send_data_frame_idx = 0 if self._write_zdle_data( send_zdata_frame_type, data, timeout ) < 0: #NO ZACK expected, continue of frame send_data_err_cnt += 1 else: send_data_err_cnt = 0 #print('tsts',ts1-ts, ts2-ts1, ts3-ts2, ts4-ts3) elif cur_state == self.SENDING_ZFIN: self._send_zfin_header(send_pos, timeout) elif cur_state == self.SENDING_ABORT_ZFIN: self._send_zfin_header(send_pos, timeout) if info_callback: info_callback("abort by receiver\n") elif cur_state == self.SENDING_ONE_DONE_AND_HAS_NEXT_FILE: if info_callback: info_callback( "one file send done, and to send next file\n") break elif cur_state == self.SENDING_SKIP: if info_callback: info_callback("file skiped by receiver\n") break elif cur_state == self.SEND_ALL_DONE or cur_state == self.SEND_ALL_DONE_ABORT: self._send_over_and_out() if info_callback: info_callback("send over and out\n") break fd.close() return True if cur_state == self.SEND_ALL_DONE else False
def recv(self, stream, crc_mode=1, retry=16, timeout=60, delay=1, quiet=0): ''' Receive a stream via the XMODEM protocol. >>> stream = file('/etc/issue', 'wb') >>> print modem.recv(stream) 2342 Returns the number of bytes received on success or ``None`` in case of failure. ''' # initiate protocol error_count = 0 char = 0 cancel = 0 while True: # first try CRC mode, if this fails, # fall back to checksum mode if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return None elif crc_mode and error_count < (retry / 2): log.debug(error.DEBUG_TRY_CRC) if not self.putc(CRC): time.sleep(delay) error_count += 1 else: log.debug(error.DEBUG_TRY_CHECKSUM) crc_mode = 0 if not self.putc(NAK): time.sleep(delay) error_count += 1 char = self.getc(1, timeout) if char is None: error_count += 1 continue elif char in [SOH, STX]: break elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 else: error_count += 1 # read data error_count = 0 income_size = 0 packet_size = 128 sequence = 1 cancel = 0 while True: while True: if char == SOH: packet_size = 128 break elif char == EOT: # Acknowledge end of transmission self.putc(ACK) return income_size elif char == CAN: # We abort if we receive two consecutive <CAN> bytes if cancel: return None else: cancel = 1 else: log.debug(error.DEBUG_EXPECT_SOH_EOT % ord(char)) error_count += 1 if error_count >= retry: self.abort() return None # read sequence error_count = 0 cancel = 0 seq1 = ord(self.getc(1)) seq2 = 0xff - ord(self.getc(1)) if seq1 == sequence and seq2 == sequence: # sequence is ok, read packet # packet_size + checksum data = self._check_crc(self.getc(packet_size + 1 + crc_mode), crc_mode) # valid data, append chunk if data: income_size += len(data) stream.write(data) self.putc(ACK) sequence = (sequence + 1) % 0x100 char = self.getc(1, timeout) continue else: # consume data self.getc(packet_size + 1 + crc_mode) log.warning(error.WARNS_SEQUENCE % (sequence, seq1, seq2)) # something went wrong, request retransmission self.putc(NAK)
def _recv_stream(self, stream, crc_mode, retry, timeout, delay): ''' Receives data and write it on a stream. It assumes the protocol has already been initialized (<CRC> or <NAK> sent and optional packet 0 received). On success it exits after an <EOT> and returns the number of bytes received. In case of failure returns ``False``. ''' # IN CASE OF YMODEM THE FILE IS ALREADY OPEN AND THE PACKET 0 RECEIVED error_count = 0 cancel = 0 sequence = 1 income_size = 0 self.putc(CRC) char = self.getc(1, timeout) while True: if char is None: error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return None else: continue elif char == CAN: if cancel: return None else: cancel = 1 elif char in [SOH, STX]: packet_size = 128 if char == SOH else 1024 # Check the requested packet size, only YMODEM has a variable # size if self.protocol != PROTOCOL_YMODEM and \ PACKET_SIZE.get(self.protocol) != packet_size: log.error(error.ABORT_PACKET_SIZE) self.abort(timeout=timeout) return False # FIXME check error seq1 = self.getc(1)[0] seq2 = 0xff - self.getc(1)[0] if seq1 == sequence and seq2 == sequence: data = self.getc(packet_size + 1 + crc_mode) data = self._check_crc(data, crc_mode) if data: # Append data to the stream income_size += len(data) stream.write(data) self.putc(ACK) sequence = (sequence + 1) % 0x100 # Waiting for new packet char = self.getc(1, timeout) continue # Sequence numbering is off or CRC is incorrect, request new # packet self.getc(packet_size + 1 + crc_mode) self.putc(NAK) elif char == EOT: # We are done, acknowledge <EOT> self.putc(ACK) return income_size elif char == CAN: # Cancel at two consecutive cancels if cancel: return False else: cancel = 1 self.putc(ACK) char = self.getc(1, timeout) continue else: log.debug(error.DEBUG_EXPECT_SOH_EOT % repr(char)) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort() return False
def _recv_stream(self, stream, crc_mode, retry, timeout, delay): ''' Receives data and write it on a stream. It assumes the protocol has already been initialized (<CRC> or <NAK> sent and optional packet 0 received). On success it exits after an <EOT> and returns the number of bytes received. In case of failure returns ``False``. ''' # IN CASE OF YMODEM THE FILE IS ALREADY OPEN AND THE PACKET 0 RECEIVED error_count = 0 cancel = 0 sequence = 1 income_size = 0 self.putc(CRC) char = self.getc(1, timeout) while True: if char is None: error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return None else: continue elif char == CAN: if cancel: return None else: cancel = 1 elif char in [SOH, STX]: packet_size = 128 if char == SOH else 1024 # Check the requested packet size, only YMODEM has a variable # size if self.protocol != PROTOCOL_YMODEM and \ PACKET_SIZE.get(self.protocol) != packet_size: log.error(error.ABORT_PACKET_SIZE) self.abort(timeout=timeout) return False seq1 = ord(self.getc(1)) seq2 = 0xff - ord(self.getc(1)) if seq1 == sequence and seq2 == sequence: data = self.getc(packet_size + 1 + crc_mode) data = self._check_crc(data, crc_mode) if data: # Append data to the stream income_size += len(data) stream.write(data) self.putc(ACK) sequence = (sequence + 1) % 0x100 # Waiting for new packet char = self.getc(1, timeout) continue # Sequence numbering is off or CRC is incorrect, request new # packet self.getc(packet_size + 1 + crc_mode) self.putc(NAK) elif char == EOT: # We are done, acknowledge <EOT> self.putc(ACK) return income_size elif char == CAN: # Cancel at two consecutive cancels if cancel: return False else: cancel = 1 self.putc(ACK) char = self.getc(1, timeout) continue else: log.debug(error.DEBUG_EXPECT_SOH_EOT % ord(char)) error_count += 1 if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort() return False
def recv(self, stream, crc_mode=1, retry=16, timeout=60, delay=1, quiet=0): ''' Receive a stream via the XMODEM protocol. >>> stream = file('/etc/issue', 'wb') >>> print modem.recv(stream) 2342 Returns the number of bytes received on success or ``None`` in case of failure. ''' # initiate protocol error_count = 0 char = 0 cancel = 0 while True: # first try CRC mode, if this fails, # fall back to checksum mode if error_count >= retry: log.error(error.ABORT_ERROR_LIMIT) self.abort(timeout=timeout) return None elif crc_mode and error_count < (retry / 2): log.debug(error.DEBUG_TRY_CRC) if not self.putc(CRC): time.sleep(delay) error_count += 1 else: log.debug(error.DEBUG_TRY_CHECKSUM) crc_mode = 0 if not self.putc(NAK): time.sleep(delay) error_count += 1 char = self.getc(1, timeout) if char is None: error_count += 1 continue elif char in [SOH, STX]: break elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 else: error_count += 1 # read data error_count = 0 income_size = 0 packet_size = 128 sequence = 1 cancel = 0 while True: while True: if char == SOH: packet_size = 128 break elif char == EOT: # Acknowledge end of transmission self.putc(ACK) return income_size elif char == CAN: # We abort if we receive two consecutive <CAN> bytes if cancel: return None else: cancel = 1 else: log.debug(error.DEBUG_EXPECT_SOH_EOT % repr(char)) error_count += 1 if error_count >= retry: self.abort() return None # read sequence error_count = 0 cancel = 0 # FIXME check error seq1 = self.getc(1)[0] seq2 = 0xff - self.getc(1)[0] if seq1 == sequence and seq2 == sequence: # sequence is ok, read packet # packet_size + checksum data = self._check_crc(self.getc(packet_size + 1 + crc_mode), crc_mode) # valid data, append chunk if data: income_size += len(data) stream.write(data) self.putc(ACK) sequence = (sequence + 1) % 0x100 char = self.getc(1, timeout) continue else: # consume data self.getc(packet_size + 1 + crc_mode) log.warning(error.WARNS_SEQUENCE % (sequence, seq1, seq2)) # something went wrong, request retransmission self.putc(NAK)
def recv(self, stream, crc_mode=1, retry=16, timeout=60, delay=1): ''' Receive a stream via the XMODEM1K protocol. >>> stream = file('/etc/issue', 'wb') >>> print modem.recv(stream) 2342 Returns the number of bytes received on success or ``None`` in case of failure. ''' # initiate protocol error_count = 0 char = 0 cancel = 0 while True: # first try CRC mode, if this fails, # fall back to checksum mode if error_count >= retry: self.abort(timeout=timeout) return None elif crc_mode and error_count < (retry / 2): if not self.putc(CRC): time.sleep(delay) error_count += 1 else: crc_mode = 0 if not self.putc(NAK): time.sleep(delay) error_count += 1 char = self.getc(1, timeout) if char is None: error_count += 1 continue elif char == SOH: #crc_mode = 0 break elif char in [STX, CAN]: break elif char == CAN: if cancel: return None else: cancel = 1 else: error_count += 1 # read data error_count = 0 income_size = 0 packet_size = 128 sequence = 1 cancel = 0 while True: while True: if char == SOH: packet_size = 128 break elif char == STX: packet_size = 1024 break elif char == EOT: # SEND LAST <ACK> self.putc(ACK) return income_size elif char == CAN: # cancel at two consecutive cancels if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 else: log.error(error.ERROR_EXPECT_SOH_EOT % ord(char)) error_count += 1 if error_count >= retry: self.abort() return None # read sequence error_count = 0 cancel = 0 seq1 = ord(self.getc(1)) seq2 = 0xff - ord(self.getc(1)) if seq1 == sequence and seq2 == sequence: # sequence is ok, read packet # packet_size + checksum data = self.getc(packet_size + 1 + crc_mode) data = self._check_crc(data, crc_mode) # valid data, append chunk if data: income_size += len(data) stream.write(data) self.putc(ACK) sequence = (sequence + 1) % 0x100 char = self.getc(1, timeout) continue else: # consume data self.getc(packet_size + 1 + crc_mode) log.debug(error.ERROR_INVALID_SEQ) # something went wrong, request retransmission self.putc(NAK)
def send(self, pattern, retry=10, timeout=60): ''' Send one or more files via the YMODEM protocol. >>> print modem.send('*.txt') True Returns ``True`` upon succesful transmission or ``False`` in case of failure. ''' # Get a list of files to send filenames = glob.glob(pattern) # filenames = 'mixer.bin' if not filenames: return True # initialize protocol error_count = 0 crc_mode = 0 start_char = self._wait_recv(error_count, timeout, retry) if start_char: crc_mode = 1 if (start_char == CRC) else 0 else: log.error(error.ABORT_PROTOCOL) # Already aborted return False for filename in filenames: # Send meta data packet sequence = 0 error_count = 0 # Add filesize filesize = os.path.getsize(filename) data = ''.join( [os.path.basename(filename), '\x00', str(filesize), '\x00']) log.debug(error.DEBUG_START_FILENAME % (filename, )) # Pick a suitable packet length for the filename packet_size = 128 if (len(data) < 128) else 1024 # Packet padding data = data.ljust(packet_size, '\0') # Calculate checksum crc = crc32_byte(list(bytearray(data.encode()))) # Emit packet if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): self.abort(timeout=timeout) return False # Wait for <CRC> before transmitting the file contents error_count = 0 if not self._wait_recv(error_count, timeout, retry): self.abort(timeout) return False filedesc = open(filename, 'rb') # AT THIS POINT # - PACKET 0 WITH METADATA TRANSMITTED # - INITIAL <CRC> OR <NAK> ALREADY RECEIVED if not self._send_stream(filedesc, crc_mode, retry, timeout): log.error(error.ABORT_SEND_STREAM) return False # AT THIS POINT # - FILE CONTENTS TRANSMITTED # - <EOT> TRANSMITTED # - <ACK> RECEIVED filedesc.close() # WAIT A <CRC> BEFORE NEXT FILE error_count = 0 if not self._wait_recv(error_count, timeout, retry): log.error(error.ABORT_INIT_NEXT) # Already aborted return False # End of batch transmission, send NULL file name sequence = 0 error_count = 0 packet_size = 128 data = '\x00' * packet_size crc = crc32_byte(list(bytearray(data.encode()))) # Emit packet if not self._send_packet(sequence, data, packet_size, crc_mode, crc, error_count, retry, timeout): log.error(error.ABORT_SEND_PACKET) # Already aborted return False # All went fine return True
def recv(self, basedir, crc_mode=1, retry=16, timeout=60, delay=1): ''' Receive some files via the YMODEM protocol and place them under ``basedir``:: >>> print modem.recv(basedir) 3 Returns the number of files received on success or ``None`` in case of failure. N.B.: currently there are no control on the existence of files, so they will be silently overwritten. ''' # Initiate protocol error_count = 0 char = 0 cancel = 0 sequence = 0 num_files = 0 while True: # First try CRC mode, if this fails, fall back to checksum mode if error_count >= retry: self.abort(timeout=timeout) return None elif crc_mode and error_count < (retry / 2): if not self.putc(CRC): time.sleep(delay) error_count += 1 else: crc_mode = 0 if not self.putc(NAK): time.sleep(delay) error_count += 1 # <CRC> or <NAK> sent, waiting answer char = self.getc(1, timeout) if char is None: error_count += 1 continue elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(error.DEBUG_RECV_CAN) cancel = 1 continue elif char in [SOH, STX]: break else: error_count += 1 continue # Receiver loop fileout = None while True: # Read next file in batch mode while True: if char is None: error_count += 1 elif char == CAN: if cancel: log.error(error.ABORT_RECV_CAN_CAN) return None else: log.debug(debug.DEBUG_RECV_CAN) cancel = 1 continue elif char in [SOH, STX]: seq1 = ord(self.getc(1)) seq2 = 0xff - ord(self.getc(1)) if seq1 == sequence and seq2 == sequence: packet_size = 128 if char == SOH else 1024 data = self.getc(packet_size + 1 + crc_mode) data = self._check_crc(data, crc_mode) if data: filename = data.split('\x00')[0] if not filename: # No filename, end of batch reception self.putc(ACK) return num_files log.info('Receiving %s to %s' % (filename, basedir)) fileout = open(os.path.join(basedir, os.path.basename(filename)), 'wb') if not fileout: log.error(error.ABORT_OPEN_FILE) self.putc(NAK) self.abort(timeout=timeout) return False else: self.putc(ACK) break # Request retransmission if something went wrong self.getc(packet_size + 1 + crc_mode) self.putc(NAK) self.getc(1, timeout) continue else: error_count += 1 self.getc(packet_size + 1 + crc_mode) self.putc(NAK) self.getc(1, timeout) stream_size = self._recv_stream(fileout, crc_mode, retry, timeout, delay) if not stream_size: log.error(error.ABORT_RECV_STREAM) return False log.debug('File transfer done, requesting next') fileout.close() num_files += 1 sequence = 0 # Ask for the next sequence and receive the reply self.putc(CRC) char = self.getc(1, timeout)