Giter Site home page Giter Site logo

ymodem's Introduction

ymodem-logo

The YMODEM project is based on XMODEM implementation written by tehmaze. It is also compatible with XMODEM mode.

license

README: ENGLISH | 简体中文

Demo

Test the sending and receiving functions

SenderAndReceiver

Interact with SecureCRT

Interact with SecureCRT as sender SecureCRT1

Interact with SecureCRT as Finder SecureCRT2

Installation

pip install ymodem

Usage

CLI TOOL

# To get help
ymodem -h
# or
python -m ymodem -h

Sending a batch of files

ymodem send ./file.bin ./file2.bin -p COM4 -b 115200
# or
python -m ymodem send ./file.bin ./file2.bin -p COM4 -b 115200

Receive a file

ymodem recv ./ -p COM4 -b 115200
# or
python -m ymodem recv ./ -p COM4 -b 115200

Source Code

from ymodem.Socket import ModemSocket

# define read
def read(size, timeout = 3):
    # implementation

# define write
def write(data, timeout = 3):
    # implementation

# create socket
cli = ModemSocket(read, write)

# send multi files
cli.send([file_path1, file_path2, file_path3 ...])

# receive multi files
cli.recv(folder_path)

For more detailed usage, please refer to main.py.

API

Create MODEM Object

def __init__(self, 
             read: Callable[[int, Optional[float]], Any], 
             write: Callable[[Union[bytes, bytearray], Optional[float]], Any], 
             protocol_type: int = ProtocolType.YMODEM, 
             protocol_type_options: List[str] = [],
             packet_size: int = 1024,
             style_id: int = _psm.get_available_styles()[0]):
  • protocol_type: Protocol type, see Protocol.py
  • protocol_type_options: such as g representing the YMODEM-G in the YMODEM protocol.
  • packet_size: The size of a single packet, 128/1024 bytes, may be adjusted depending on the protocol style
  • style_id: Protocol style, different styles have different support for functional features

Send files

def send(self, 
         paths: List[str], 
         callback: Optional[Callable[[int, str, int, int], None]] = None
        ) -> bool:
  • callback: callback function. see below.

    Parameter Description
    task index index of current task
    task (file) name name of the file
    total packets number of packets plan to send
    success packets number of packets successfully sent

Receive files

def recv(self, 
         path: str, 
         callback: Optional[Callable[[int, str, int, int], None]] = None
        ) -> bool:
  • path: folder path for storing the target file
  • callback: callback function. Same as the callback of send().

ATTENTION

Depending on different communication environments, developers may need to manually adjust timeout parameters in _read_and_wait or _write_and_wait.

Debug

If you want to output debugging information, set the log level to DEBUG.

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

Changelog

v1.5 (2024/02/03)

  • Added cli tool to iteract with YMODEM via Serial bus

v1.5 (2023/05/20 11:00 +00:00)

  • Rewritten send() and recv()
  • Support YMODEM-G. The success rate of YMODEM-G based on pyserial depends on the user's OS, and after testing, the success rate is very low without any delay.

License

MIT License

ymodem's People

Contributors

alexwoo1900 avatar crvux avatar edgarigl avatar frederiklauber avatar o-murphy avatar otscher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ymodem's Issues

Transmission fails to recover from byte error

I've gotten transmission up and running with the included FileReceiver.py/FileSender.py, however the connection fails to gracefully handle byte errors (or my methods are wrong).

For instance, modfiying FileReceiver.py:

    txcount = 0
    def read(size, timeout = 10):
        global txcount
        serial_io.timeout = timeout
        txcount +=1
        if txcount == 300:
            data = bytearray(serial_io.read(size))
            data[0] = b'0'
            return bytes(data)
        else:
            return serial_io.read(size)

Basically, if I just mangle the first byte of the 300th receive, I get

$python FileReceiver.py 
0 - sample.stl 26.00% [#############->.....................................]4.24s[Modem]: Read timeout!
[Receiver]: Received data timed out.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: Send a request for retransmission.
[Receiver]: Wrong sequence, drop the whole packet.
[Receiver]: The number of retransmissions has reached the maximum limit, abort and exit!

on the receive side and on the transmit side:

$python FileSender.py
0 - sample.stl 26.00% [#############->.....................................]4.23s[Sender]: No ACK from Receiver, 
preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: No ACK from Receiver, preparing to retransmit.
[Sender]: The number of retransmissions has reached the maximum limit, abort and exit!

I get similar behavior if I do the same mangling on the TX side.

Alternative progressbar

What do you think about usage of qtdm or rich.progress instead of your default progressbar example? It can be more informative

ymodem没有实现对Final packet的处理

这个ymodem还不支持批量文件传输,关键是传输单个文件时也没有在文件传输的结尾传输SOH的空包。
这个程序自己收发没有问题,但是不能和其它程序兼容。

Unrequested response

Log is

[Receiver]: CRC ->
[Receiver]: CRC ->
[Receiver]: CRC ->
[Receiver]: CRC ->
[Receiver]: CRC ->
[Receiver]: CRC ->
[Receiver]: CRC ->

image

运行一直有问题,不知道哪里出问题了

发送端:
`
ser=serial.Serial("COM5", 57600)

def getc(size):
return ser.read(size) or None

def putc(data):
return ser.write(data)

tester = YModem(getc, putc)

tester.send_file("test1.txt")
`

接收端:
`
import serial
from YModem import YModem

serial_io=serial.Serial("/dev/ttyS2",57600)

def getc(size):
return serial_io.read(size) or None

def putc(data):
return serial_io.write(data)

tester = YModem(getc, putc)

if not serial_io.isOpen():
serial_io.open()

tester.recv_file("test.txt")
`

发送端是windows,接收端是linux系统的单片机。。。
然后,一直收不到文件。
发送端没有任何显示,接收端显示:
`
root@OpenWrt:~# python testrec02y.py
2020-08-22 15:05:23,670 - DEBUG - <<< CRC

`

不知道哪里出问题了。。。

YModem.py line:50 "self.log.debug("Size: " + str(file_sent) + "Bytes")"的问题

def send_file(self, file_path, retry=20, callback=None):
        try:
            file_stream = open(file_path, 'rb')
            file_name = os.path.basename(file_path)
            file_size = os.path.getsize(file_path)
            file_sent = self.send(file_stream, file_name, file_size, retry, callback)
        except IOError as e:
            self.log.error(str(e))
        finally:
            file_stream.close()
        
        self.log.debug("Task Done!")
        self.log.debug("File: " + file_name)
        self.log.debug("Size: " + str(file_sent) + "Bytes")
        self.log.debug("Packets: " + str(self.st.get_valid_sent_packets()))
        return file_sent

self.log.debug("Size: " + str(file_sent) + "Bytes") ,这里file_sent是否应该是file_size

Sender EOT issue

The library has an issue with ymodem protocol compatibility (it's about how the sender approves the EOT)

The ymodem protocol requires 2 time approvement off EOT

It have been kinda:

# this is the reference

Sender: -> EOT (initial EOT char)
Sender: <- read_and_wait for NAK
Sender: -> EOT (second EOT char)
Sender: <- read_and_wait for ACK


# there the transmitting completely done
# btw some receivers sends additional packet with 'C' char after an EOT complete, and also returns additional packet with filename and filesize back to the sender. And not complete the transmission if that packets have not an answer from sender

# so the minimal reference might look kinda:

Sender: -> EOT (initial EOT char)
Sender: <- read_and_wait for NAK
Sender: -> EOT (second EOT char)
Sender: <- read_and_wait for ACK
Sender: <- read_and_wait for CRC

Now I can't find why some receivers are not close the transmittion without that steps. But I already have a fix for that
I tested it and it completely works with my receiver and also with the Hyperterminal on it's receiver mode

The block of the code that have to be modified

'''
                2. YMODEM MINIMUM REQUIREMENTS

                + At the end of each file, the sending program shall send EOT up to ten
                times until it receives an ACK character. (This is part of the
                XMODEM spec.)

                7.3.3 Sending_program_considerations

                When the sender has no more data, it sends an <eot>, and awaits an <ack>,
                resending the <eot> if it doesn't get one. Again, the protocol could be
                receiver-driven, with the sender only having the high-level 1-minute
                timeout to abort.
                '''

                '''
                Some receivers are waiting for EOT twice:
                
                [Sender]: <- ACK
                [Sender]: Reached EOF
                [Sender]: EOT ->
                [Sender]: <- ACK
                '''

                retries = 0
                while True:
                    if retries < 10:
                        # c = self._write_and_wait(EOT, [ACK])
                        c = self._write_and_wait(EOT, [NAK, ACK])
                        self.logger.debug("[Sender]: EOT ->")

                        if c:

                            if c == NAK:
                                self.logger.debug("[Sender]: <- NAK")

                            elif c == ACK:
                                self.logger.debug("[Sender]: <- ACK")
                                break

                            else:
                                self.logger.warning("[Sender]: No ACK or NAK from Receiver, preparing to retransmit.")
                                retries += 1

                        else:
                            self.logger.warning("[Sender]: No ACK or NAK from Receiver, preparing to retransmit.")
                            retries += 1

                    else:
                        self.logger.error(
                            "[Sender]: The number of retransmissions has reached the maximum limit, abort and exit!")
                        self._abort()
                        return False

                '''
                Some receivers are sending CRC after EOT:
                
                [Sender]: <- ACK
                [Sender]: Reached EOF
                [Sender]: EOT ->
                [Sender]: <- ACK
                '''

                retries = 0
                while True:
                    if retries < 10:
                        c = self._read_and_wait([CRC])

                        if c:
                            self.logger.debug(
                                "[Sender]: <- CRC"
                            )
                            break
                        else:
                            self.logger.debug(c)
                            retries += 1
                    else:
                        self.logger.error(
                            "[Sender]: The number of retransmissions has reached the maximum limit, abort and exit!")
                        self._abort()
                        return False

Modem接受bug

modem.py 的567行
data = self.reader.read(packet_size + 1 + crc_mode, timeout)
valid, data = self._verify_recv_checksum(crc_mode, data)

其中data 不能保证接受成功啊,得加个校验语句,以保证后面不出错。
if data and len(data)>=2:
(说多了都是泪)

How is best to install this package?

I notice this package isn't on pypi and also doesn't come with a setup.py file. What's the best strategy to install it locally to try it out?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.