/v2/fast_io/iorw_to.py

https://github.com/peterhinch/micropython-async · Python · 143 lines · 106 code · 19 blank · 18 comment · 21 complexity · ec5a0f0a660e38b3a5a3ed7fbfc3c763 MD5 · raw file

  1. # iorw_to.py Emulate a device which can read and write one character at a time
  2. # and test timeouts.
  3. # Copyright (c) Peter Hinch 2019
  4. # Released under the MIT licence
  5. # This requires the modified version of uasyncio (fast_io directory).
  6. # Slow hardware is emulated using timers.
  7. # MyIO.write() ouputs a single character and sets the hardware not ready.
  8. # MyIO.readline() returns a single character and sets the hardware not ready.
  9. # Timers asynchronously set the hardware ready.
  10. import io, pyb
  11. import uasyncio as asyncio
  12. import micropython
  13. import sys
  14. try:
  15. print('Uasyncio version', asyncio.version)
  16. if not isinstance(asyncio.version, tuple):
  17. print('Please use fast_io version 0.24 or later.')
  18. sys.exit(0)
  19. except AttributeError:
  20. print('ERROR: This test requires the fast_io version. It will not run correctly')
  21. print('under official uasyncio V2.0 owing to a bug which prevents concurrent')
  22. print('input and output.')
  23. sys.exit(0)
  24. print('Issue iorw_to.test(True) to test ioq, iorw_to.test() to test runq.')
  25. print('Test runs until interrupted. Tasks time out after 15s.')
  26. print('Issue ctrl-d after each run.')
  27. micropython.alloc_emergency_exception_buf(100)
  28. MP_STREAM_POLL_RD = const(1)
  29. MP_STREAM_POLL_WR = const(4)
  30. MP_STREAM_POLL = const(3)
  31. MP_STREAM_ERROR = const(-1)
  32. def printbuf(this_io):
  33. print(bytes(this_io.wbuf[:this_io.wprint_len]).decode(), end='')
  34. class MyIO(io.IOBase):
  35. def __init__(self, read=False, write=False):
  36. self.ready_rd = False # Read and write not ready
  37. self.rbuf = b'ready\n' # Read buffer
  38. self.ridx = 0
  39. pyb.Timer(4, freq = 5, callback = self.do_input)
  40. self.wch = b''
  41. self.wbuf = bytearray(100) # Write buffer
  42. self.wprint_len = 0
  43. self.widx = 0
  44. pyb.Timer(5, freq = 10, callback = self.do_output)
  45. # Read callback: emulate asynchronous input from hardware.
  46. # Typically would put bytes into a ring buffer and set .ready_rd.
  47. def do_input(self, t):
  48. self.ready_rd = True # Data is ready to read
  49. # Write timer callback. Emulate hardware: if there's data in the buffer
  50. # write some or all of it
  51. def do_output(self, t):
  52. if self.wch:
  53. self.wbuf[self.widx] = self.wch
  54. self.widx += 1
  55. if self.wch == ord('\n'):
  56. self.wprint_len = self.widx # Save for schedule
  57. micropython.schedule(printbuf, self)
  58. self.widx = 0
  59. self.wch = b''
  60. def ioctl(self, req, arg): # see ports/stm32/uart.c
  61. ret = MP_STREAM_ERROR
  62. if req == MP_STREAM_POLL:
  63. ret = 0
  64. if arg & MP_STREAM_POLL_RD:
  65. if self.ready_rd:
  66. ret |= MP_STREAM_POLL_RD
  67. if arg & MP_STREAM_POLL_WR:
  68. if not self.wch:
  69. ret |= MP_STREAM_POLL_WR # Ready if no char pending
  70. return ret
  71. # Test of device that produces one character at a time
  72. def readline(self):
  73. self.ready_rd = False # Cleared by timer cb do_input
  74. ch = self.rbuf[self.ridx]
  75. if ch == ord('\n'):
  76. self.ridx = 0
  77. else:
  78. self.ridx += 1
  79. return chr(ch)
  80. # Emulate unbuffered hardware which writes one character: uasyncio waits
  81. # until hardware is ready for the next. Hardware ready is emulated by write
  82. # timer callback.
  83. def write(self, buf, off, sz):
  84. self.wch = buf[off] # Hardware starts to write a char
  85. return 1 # 1 byte written. uasyncio waits on ioctl write ready
  86. # Note that trapping the exception and returning is still mandatory.
  87. async def receiver(myior):
  88. sreader = asyncio.StreamReader(myior)
  89. try:
  90. while True:
  91. res = await sreader.readline()
  92. print('Received', res)
  93. except asyncio.TimeoutError:
  94. print('Receiver timeout')
  95. async def sender(myiow):
  96. swriter = asyncio.StreamWriter(myiow, {})
  97. await asyncio.sleep(1)
  98. count = 0
  99. try: # Trap in outermost scope to catch cancellation of .sleep
  100. while True:
  101. count += 1
  102. tosend = 'Wrote Hello MyIO {}\n'.format(count)
  103. await swriter.awrite(tosend.encode('UTF8'))
  104. await asyncio.sleep(2)
  105. except asyncio.TimeoutError:
  106. print('Sender timeout')
  107. async def run(coro, t):
  108. await asyncio.wait_for_ms(coro, t)
  109. async def do_test(loop, t):
  110. myio = MyIO()
  111. while True:
  112. tr = t * 1000 + (pyb.rng() >> 20) # Add ~1s uncertainty
  113. tw = t * 1000 + (pyb.rng() >> 20)
  114. print('Timeouts: {:7.3f}s read {:7.3f}s write'.format(tr/1000, tw/1000))
  115. loop.create_task(run(receiver(myio), tr))
  116. await run(sender(myio), tw)
  117. await asyncio.sleep(2) # Wait out timing randomness
  118. def test(ioq=False):
  119. if ioq:
  120. loop = asyncio.get_event_loop(ioq_len=16)
  121. else:
  122. loop = asyncio.get_event_loop()
  123. loop.create_task(do_test(loop, 15))
  124. loop.run_forever()