From 971e9f7ff197446f20f072f2850f439854881844 Mon Sep 17 00:00:00 2001 From: slush0 Date: Thu, 31 Mar 2016 22:31:56 +0200 Subject: [PATCH] Lightly refactored uasyncio, added logging Demo animations --- src/layout.py | 2 +- src/lib/logging.py | 75 +++++++++++++ src/lib/uasyncio/__init__.py | 0 src/{ => lib}/uasyncio/core.py | 100 ++++++++--------- src/lib/uasyncio/epoll.py | 73 ++++++++++++ src/{ => lib}/uasyncio/queues.py | 0 .../__init__.py => lib/uasyncio/stream.py} | 93 +++------------- src/playground/__init__.py | 104 +++++++++--------- 8 files changed, 261 insertions(+), 186 deletions(-) create mode 100644 src/lib/logging.py create mode 100644 src/lib/uasyncio/__init__.py rename src/{ => lib}/uasyncio/core.py (70%) create mode 100644 src/lib/uasyncio/epoll.py rename src/{ => lib}/uasyncio/queues.py (100%) rename src/{uasyncio/__init__.py => lib/uasyncio/stream.py} (58%) diff --git a/src/layout.py b/src/layout.py index 64a2e7a9d4..3b8c90ae3a 100644 --- a/src/layout.py +++ b/src/layout.py @@ -36,4 +36,4 @@ def show_send(address, amount, currency='BTC'): ui.display.text(10, 80, '%f %s' % (amount, currency), BOLD, BLACK, WHITE) ui.display.text(10, 110, 'to this address:', NORMAL, BLACK, WHITE) ui.display.text(10, 140, address[:18], MONO, BLACK, WHITE) - ui.display.text(10, 160, address[18:], MONO, BLACK, WHITE) + ui.display.text(10, 160, address[18:], MONO, BLACK, WHITE) \ No newline at end of file diff --git a/src/lib/logging.py b/src/lib/logging.py new file mode 100644 index 0000000000..1c3ef0d847 --- /dev/null +++ b/src/lib/logging.py @@ -0,0 +1,75 @@ +import sys + +CRITICAL = 50 +ERROR = 40 +WARNING = 30 +INFO = 20 +DEBUG = 10 +NOTSET = 0 + +_level_dict = { + CRITICAL: "CRIT", + ERROR: "ERROR", + WARNING: "WARN", + INFO: "INFO", + DEBUG: "DEBUG", +} + +_stream = sys.stderr + +class Logger: + + def __init__(self, name): + self.level = NOTSET + self.name = name + + def _level_str(self, level): + if level in _level_dict: + return _level_dict[level] + return "LVL" + str(level) + + def log(self, level, msg, *args): + if level >= (self.level or _level): + print(("%s:%s:" + msg) % ((self._level_str(level), self.name) + args), file=_stream) + + def debug(self, msg, *args): + self.log(DEBUG, msg, *args) + + def info(self, msg, *args): + self.log(INFO, msg, *args) + + def warning(self, msg, *args): + self.log(WARNING, msg, *args) + + def error(self, msg, *args): + self.log(ERROR, msg, *args) + + def critical(self, msg, *args): + self.log(CRITICAL, msg, *args) + + +_level = INFO +_loggers = {} + +def getLogger(name): + if name in _loggers: + return _loggers[name] + l = Logger(name) + _loggers[name] = l + return l + +def info(msg, *args): + getLogger(None).info(msg, *args) + +def debug(msg, *args): + getLogger(None).debug(msg, *args) + +def basicConfig(level=INFO, filename=None, stream=None, format=None): + global _level, _stream + _level = level + if stream: + _stream = stream + if filename is not None: + print("logging.basicConfig: filename arg is not supported") + if format is not None: + print("logging.basicConfig: format arg is not supported") diff --git a/src/lib/uasyncio/__init__.py b/src/lib/uasyncio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/uasyncio/core.py b/src/lib/uasyncio/core.py similarity index 70% rename from src/uasyncio/core.py rename to src/lib/uasyncio/core.py index 62b5b68fad..cfbd567845 100644 --- a/src/uasyncio/core.py +++ b/src/lib/uasyncio/core.py @@ -1,12 +1,9 @@ -try: - import utime as time -except ImportError: - import time -import uheapq as heapq -import logging +import utime +import uheapq - -log = logging.getLogger("asyncio") +if __debug__: + import logging + log = logging.getLogger("asyncio") type_gen = type((lambda: (yield))()) @@ -15,44 +12,44 @@ class EventLoop: def __init__(self): self.q = [] self.cnt = 0 - self.button_cb = None - - def time(self): - return time.time() + #self.button_cb = None + ''' def create_task(self, coro): # CPython 3.4.2 self.call_at(0, coro) # CPython asyncio incompatibility: we don't return Task object + ''' def call_soon(self, callback, *args): self.call_at(0, callback, *args) def call_later(self, delay, callback, *args): - self.call_at(self.time() + delay, callback, *args) + self.call_at(utime.time() + delay, callback, *args) def call_at(self, time, callback, *args): # Including self.cnt is a workaround per heapq docs if __debug__: log.debug("Scheduling %s", (time, self.cnt, callback, args)) - heapq.heappush(self.q, (time, self.cnt, callback, args)) + uheapq.heappush(self.q, (time, self.cnt, callback, args)) # print(self.q) self.cnt += 1 def wait(self, delay): # Default wait implementation, to be overriden in subclasses # with IO scheduling - log.debug("Sleeping for: %s", delay) - time.sleep(delay) + if __debug__: + log.debug("Sleeping for: %s", delay) + utime.sleep(delay) def run_forever(self): while True: if self.q: - t, cnt, cb, args = heapq.heappop(self.q) + t, cnt, cb, args = uheapq.heappop(self.q) if __debug__: log.debug("Next coroutine to run: %s", (t, cnt, cb, args)) # __main__.mem_info() - tnow = self.time() + tnow = utime.time() delay = t - tnow if delay > 0: self.wait(delay) @@ -76,26 +73,29 @@ class EventLoop: arg = ret.arg if isinstance(ret, Sleep): delay = arg + elif isinstance(ret, StopLoop): + return arg + ''' elif isinstance(ret, IORead): -# self.add_reader(ret.obj.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, ret.obj) -# self.add_reader(ret.obj.fileno(), lambda c, f: self.call_soon(c, f), cb, ret.obj) -# self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb) + self.add_reader(ret.obj.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, ret.obj) + self.add_reader(ret.obj.fileno(), lambda c, f: self.call_soon(c, f), cb, ret.obj) + self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb) self.add_reader(arg.fileno(), cb) continue elif isinstance(ret, IOWrite): -# self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb) + self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb) self.add_writer(arg.fileno(), cb) continue elif isinstance(ret, IOReadDone): self.remove_reader(arg.fileno()) elif isinstance(ret, IOWriteDone): self.remove_writer(arg.fileno()) - elif isinstance(ret, StopLoop): - return arg - elif isinstance(ret, IOButton): - print("TADY") - self.button_cb = cb - continue + ''' + + #elif isinstance(ret, IOButton): + # print("TADY") + # self.button_cb = cb + # continue elif isinstance(ret, type_gen): self.call_soon(ret) elif ret is None: @@ -109,17 +109,19 @@ class EventLoop: continue self.call_later(delay, cb, *args) + ''' def run_until_complete(self, coro): def _run_and_stop(): yield from coro yield StopLoop(0) self.call_soon(_run_and_stop()) self.run_forever() - - def close(self): - pass + ''' + #def close(self): + # pass +''' class SysCall: def __init__(self, *args): @@ -127,22 +129,25 @@ class SysCall: def handle(self): raise NotImplementedError - +''' + # Optimized syscall with 1 arg -class SysCall1(SysCall): - +class SysCall1: def __init__(self, arg): self.arg = arg -class IOButton(SysCall): + def handle(self): + raise NotImplementedError + +#class IOButton(SysCall): +# pass + +class StopLoop(SysCall1): pass class Sleep(SysCall1): pass -class StopLoop(SysCall1): - pass - class IORead(SysCall1): pass @@ -155,7 +160,6 @@ class IOReadDone(SysCall1): class IOWriteDone(SysCall1): pass - _event_loop = None _event_loop_class = EventLoop def get_event_loop(): @@ -167,21 +171,7 @@ def get_event_loop(): def sleep(secs): yield Sleep(secs) +''' def coroutine(f): return f - -# -# The functions below are deprecated in uasyncio, and provided only -# for compatibility with CPython asyncio -# - -def async(coro, loop=_event_loop): - _event_loop.call_soon(coro) - # CPython asyncio incompatibility: we don't return Task object - return coro - - -# CPython asyncio incompatibility: Task is a function, not a class (for efficiency) -def Task(coro, loop=_event_loop): - # Same as async() - _event_loop.call_soon(coro) +''' diff --git a/src/lib/uasyncio/epoll.py b/src/lib/uasyncio/epoll.py new file mode 100644 index 0000000000..dd2d328c57 --- /dev/null +++ b/src/lib/uasyncio/epoll.py @@ -0,0 +1,73 @@ +import uselect +import errno + +from .core import EventLoop + +if __debug__: + import logging + log = logging.getLogger("asyncio") + +class EpollEventLoop(EventLoop): + + def __init__(self): + EventLoop.__init__(self) + self.poller = uselect.poll() + self.objmap = {} + + def add_reader(self, fd, cb, *args): + if __debug__: + log.debug("add_reader%s", (fd, cb, args)) + if args: + self.poller.register(fd, uselect.POLLIN) + self.objmap[fd] = (cb, args) + else: + self.poller.register(fd, uselect.POLLIN) + self.objmap[fd] = cb + + def remove_reader(self, fd): + if __debug__: + log.debug("remove_reader(%s)", fd) + self.poller.unregister(fd) + del self.objmap[fd] + + def add_writer(self, fd, cb, *args): + if __debug__: + log.debug("add_writer%s", (fd, cb, args)) + if args: + self.poller.register(fd, uselect.POLLOUT) + self.objmap[fd] = (cb, args) + else: + self.poller.register(fd, uselect.POLLOUT) + self.objmap[fd] = cb + + def remove_writer(self, fd): + if __debug__: + log.debug("remove_writer(%s)", fd) + try: + self.poller.unregister(fd) + self.objmap.pop(fd, None) + except OSError as e: + # StreamWriter.awrite() first tries to write to an fd, + # and if that succeeds, yield IOWrite may never be called + # for that fd, and it will never be added to poller. So, + # ignore such error. + if e.args[0] != errno.ENOENT: + raise + + def wait(self, delay): + if __debug__: + log.debug("epoll.wait(%d)", delay) + # We need one-shot behavior (second arg of 1 to .poll()) + if delay == -1: + res = self.poller.poll(-1, 1) + else: + res = self.poller.poll(int(delay * 1000), 1) + # log.debug("epoll result: %s", res) + for fd, ev in res: + cb = self.objmap[fd] + if __debug__: + log.debug("Calling IO callback: %r", cb) + if isinstance(cb, tuple): + cb[0](*cb[1]) + else: + self.call_soon(cb) diff --git a/src/uasyncio/queues.py b/src/lib/uasyncio/queues.py similarity index 100% rename from src/uasyncio/queues.py rename to src/lib/uasyncio/queues.py diff --git a/src/uasyncio/__init__.py b/src/lib/uasyncio/stream.py similarity index 58% rename from src/uasyncio/__init__.py rename to src/lib/uasyncio/stream.py index 504f1e9288..1edf849b85 100644 --- a/src/uasyncio/__init__.py +++ b/src/lib/uasyncio/stream.py @@ -1,74 +1,10 @@ +import usocket import errno -import uselect as select -import usocket as _socket -from uasyncio.core import * - - -class EpollEventLoop(EventLoop): - - def __init__(self): - EventLoop.__init__(self) - self.poller = select.poll() - self.objmap = {} - - def add_reader(self, fd, cb, *args): - if __debug__: - log.debug("add_reader%s", (fd, cb, args)) - if args: - self.poller.register(fd, select.POLLIN) - self.objmap[fd] = (cb, args) - else: - self.poller.register(fd, select.POLLIN) - self.objmap[fd] = cb - - def remove_reader(self, fd): - if __debug__: - log.debug("remove_reader(%s)", fd) - self.poller.unregister(fd) - del self.objmap[fd] - - def add_writer(self, fd, cb, *args): - if __debug__: - log.debug("add_writer%s", (fd, cb, args)) - if args: - self.poller.register(fd, select.POLLOUT) - self.objmap[fd] = (cb, args) - else: - self.poller.register(fd, select.POLLOUT) - self.objmap[fd] = cb - - def remove_writer(self, fd): - if __debug__: - log.debug("remove_writer(%s)", fd) - try: - self.poller.unregister(fd) - self.objmap.pop(fd, None) - except OSError as e: - # StreamWriter.awrite() first tries to write to an fd, - # and if that succeeds, yield IOWrite may never be called - # for that fd, and it will never be added to poller. So, - # ignore such error. - if e.args[0] != errno.ENOENT: - raise - - def wait(self, delay): - if __debug__: - log.debug("epoll.wait(%d)", delay) - # We need one-shot behavior (second arg of 1 to .poll()) - if delay == -1: - res = self.poller.poll(-1, 1) - else: - res = self.poller.poll(int(delay * 1000), 1) - #log.debug("epoll result: %s", res) - for fd, ev in res: - cb = self.objmap[fd] - if __debug__: - log.debug("Calling IO callback: %r", cb) - if isinstance(cb, tuple): - cb[0](*cb[1]) - else: - self.call_soon(cb) +from .core import IOReadDone, IOWriteDone, IORead, IOWrite +if __debug__: + import logging + log = logging.getLogger("asyncio") class StreamReader: @@ -141,7 +77,7 @@ class StreamWriter: buf = buf[res:] sz -= res yield IOWrite(self.s) - #assert s2.fileno() == self.s.fileno() + # assert s2.fileno() == self.s.fileno() if __debug__: log.debug("StreamWriter.awrite(): can write more") @@ -159,9 +95,9 @@ class StreamWriter: def open_connection(host, port): if __debug__: log.debug("open_connection(%s, %s)", host, port) - s = _socket.socket() + s = usocket.socket() s.setblocking(False) - ai = _socket.getaddrinfo(host, port) + ai = usocket.getaddrinfo(host, port) addr = ai[0][4] try: s.connect(addr) @@ -179,13 +115,14 @@ def open_connection(host, port): def start_server(client_coro, host, port, backlog=10): - log.debug("start_server(%s, %s)", host, port) - s = _socket.socket() + if __debug__: + log.debug("start_server(%s, %s)", host, port) + s = usocket.socket() s.setblocking(False) - ai = _socket.getaddrinfo(host, port) + ai = usocket.getaddrinfo(host, port) addr = ai[0][4] - s.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) + s.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1) s.bind(addr) s.listen(backlog) while True: @@ -200,7 +137,3 @@ def start_server(client_coro, host, port, backlog=10): log.debug("start_server: After accept: %s", s2) extra = {"peername": client_addr} yield client_coro(StreamReader(s2), StreamWriter(s2, extra)) - - -import uasyncio.core -uasyncio.core._event_loop_class = EpollEventLoop diff --git a/src/playground/__init__.py b/src/playground/__init__.py index 87c155824e..0b7193daca 100644 --- a/src/playground/__init__.py +++ b/src/playground/__init__.py @@ -1,47 +1,42 @@ # import time +import sys +sys.path.append('lib') + import gc import utime import logging -import uasyncio +# import uasyncio +import math from uasyncio import core from TrezorUi import Display +from trezor import ui + from . import utils d = Display() logging.basicConfig(level=logging.INFO) -loop = uasyncio.get_event_loop() +loop = core.get_event_loop() -if __debug__: - def meminfo(): - mem_free = gc.mem_free() - gc.collect() - print("free_mem: %s/%s, collect: %s" % (mem_free, gc.mem_free(), gc.collect())) - loop.call_later(1, meminfo) - - # meminfo() - -def animate(col): - - col %= 0xff - col += 0x0f +def meminfo(): + mem_free = gc.mem_free() + collected = gc.collect() + print("free_mem: %s/%s, collect: %s" % (mem_free, gc.mem_free(), collected)) + loop.call_later(1, meminfo) +def animate(): + col = 0 f = open('../assets/lock.toi', 'r') - d.icon(10, 170, f.read(), utils.rgb2color(0, col, 0), 0xffff) - loop.call_later(0.5, animate, col) + while True: + col %= 0xff + col += 0x0f -def animate2(col): + d.icon(190, 170, f.read(), utils.rgb2color(col, 0, 0), 0xffff) + f.seek(0) - col %= 0xff - col += 0x0f - - # yield True - f = open('../assets/lock.toi', 'r') - d.icon(170, 170, f.read(), utils.rgb2color(col, 0, 0), 0xffff) - - loop.call_later(0.1, animate2, col) + yield from core.sleep(0.5) sec = 0 event = None @@ -50,19 +45,15 @@ def sekunda(x): print('Sekunda %d' % sec) - if sec == x: - loop.call_soon(loop.button_cb, 'levy') - loop.button_cb = None + if sec == 0: + # if loop.button_cb: + # loop.call_soon(loop.button_cb, 'levy') + # loop.button_cb = None + return sec += 1 loop.call_later(1, sekunda, x) - - # global event - # event = wait_for() - # event.__next__() - - def wait_for(): print("Jsem tady") @@ -71,23 +62,36 @@ def wait_for(): print("Po cekani na event") +def tap_to_confirm(): + STEP_X = 0.07 + DELAY = 0.01 + BASE_COLOR = (0x00, 0x00, 0x00) + MIN_COLOR = 0x00 + MAX_COLOR = 0xB0 + + _background = utils.rgb2color(255, 255, 255) + x = math.pi + while True: + x += STEP_X + if x > 2 * math.pi: + x -= 2 * math.pi + y = 1 + math.sin(x) + + # Normalize color from interval 0:2 to MIN_COLOR:MAX_COLOR + col = int((MAX_COLOR - MIN_COLOR) / 2 * y) + MIN_COLOR + foreground = utils.rgb2color(BASE_COLOR[0] + col, BASE_COLOR[1] + col, BASE_COLOR[2] + col) + + ui.display.text(10, 220, 'TAP TO CONFIRM', 2, foreground, _background) + + yield from core.sleep(DELAY) + def run(): - ''' - d = Display() - d.bar(0, 0, 240, 240, 0) + # sekunda(3) + # loop.call_soon(wait_for()) - f = open('../assets/trezor.toi', 'r') - d.image(0, 0, f.read()) - ''' + loop.call_soon(meminfo) + loop.call_soon(tap_to_confirm()) + loop.call_soon(animate()) - # logging.basicConfig(level=logging.INFO) - - sekunda(3) - - loop.call_soon(animate, 0x0000) - loop.call_soon(animate2, 0x00ff) - - loop.call_soon(wait_for()) - loop.run_forever() loop.close()