2016-04-28 05:43:37 +00:00
|
|
|
import utime
|
|
|
|
|
2016-05-16 15:10:12 +00:00
|
|
|
from uheapq import heappop, heappush, heapify
|
2016-04-28 05:43:37 +00:00
|
|
|
from .utils import type_gen
|
2016-04-29 12:49:47 +00:00
|
|
|
from . import msg
|
2016-04-29 20:24:27 +00:00
|
|
|
from . import log
|
2016-04-28 05:43:37 +00:00
|
|
|
|
2016-04-29 19:48:59 +00:00
|
|
|
if __debug__:
|
|
|
|
# For performance stats
|
|
|
|
import array
|
|
|
|
log_delay_pos = 0
|
|
|
|
log_delay_rb_len = const(10)
|
|
|
|
log_delay_rb = array.array('i', [0] * log_delay_rb_len)
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
# Touch interface
|
|
|
|
TOUCH = const(256) # 0-255 is reserved for USB interfaces
|
2016-05-12 14:18:40 +00:00
|
|
|
TOUCH_START = const(1)
|
|
|
|
TOUCH_MOVE = const(2)
|
2016-05-30 14:40:46 +00:00
|
|
|
TOUCH_END = const(4)
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-06-17 18:52:36 +00:00
|
|
|
msg_handlers = {} # Message interface -> [generator]
|
2016-05-30 14:15:34 +00:00
|
|
|
time_queue = []
|
2016-05-16 15:10:12 +00:00
|
|
|
time_ticket = 0
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-05-02 14:22:22 +00:00
|
|
|
|
2016-05-12 14:18:40 +00:00
|
|
|
def schedule(gen, data=None, time=None):
|
2016-05-16 15:10:12 +00:00
|
|
|
global time_ticket
|
2016-04-29 19:48:59 +00:00
|
|
|
if not time:
|
|
|
|
time = utime.ticks_us()
|
2016-05-16 15:10:12 +00:00
|
|
|
heappush(time_queue, (time, time_ticket, gen, data))
|
|
|
|
time_ticket += 1
|
|
|
|
return gen
|
|
|
|
|
|
|
|
|
|
|
|
def unschedule(gen):
|
|
|
|
global time_queue
|
|
|
|
time_queue = [entry for entry in time_queue if entry[1] is not gen]
|
|
|
|
heapify(time_queue)
|
|
|
|
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
def block(gen, iface):
|
2016-06-17 18:52:36 +00:00
|
|
|
if iface in msg_handlers:
|
|
|
|
msg_handlers[iface].append(gen)
|
|
|
|
else:
|
|
|
|
msg_handlers[iface] = [gen]
|
2016-05-16 15:10:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
def unblock(gen):
|
2016-05-30 14:15:34 +00:00
|
|
|
for iface in msg_handlers:
|
2016-06-17 18:52:36 +00:00
|
|
|
if gen in msg_handlers[iface]:
|
|
|
|
msg_handlers[iface].remove(gen)
|
2016-05-12 14:18:40 +00:00
|
|
|
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
class Syscall():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Sleep(Syscall):
|
2016-05-12 14:18:40 +00:00
|
|
|
|
|
|
|
def __init__(self, us):
|
|
|
|
self.time = utime.ticks_us() + us
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
def register(self, gen):
|
|
|
|
schedule(gen, self, self.time)
|
|
|
|
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
class Select(Syscall):
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
def __init__(self, iface):
|
|
|
|
self.iface = iface
|
2016-04-29 23:20:57 +00:00
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
def register(self, gen):
|
|
|
|
block(gen, self.iface)
|
2016-05-16 15:10:12 +00:00
|
|
|
|
2016-05-02 14:22:22 +00:00
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
class Wait(Syscall):
|
2016-05-11 12:39:57 +00:00
|
|
|
|
2016-05-02 14:06:08 +00:00
|
|
|
def __init__(self, gens, wait_for=1, exit_others=True):
|
2016-05-16 15:10:12 +00:00
|
|
|
self.gens = gens
|
2016-05-02 14:06:08 +00:00
|
|
|
self.wait_for = wait_for
|
|
|
|
self.exit_others = exit_others
|
2016-06-17 18:52:36 +00:00
|
|
|
self.scheduled = [] # In uPython, set() cannot contain generators
|
2016-05-25 12:27:22 +00:00
|
|
|
self.finished = []
|
2016-05-02 14:06:08 +00:00
|
|
|
self.callback = None
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
def register(self, gen):
|
|
|
|
self.scheduled = [schedule(self._wait(g)) for g in self.gens]
|
2016-05-16 15:10:12 +00:00
|
|
|
self.callback = gen
|
2016-05-02 14:06:08 +00:00
|
|
|
|
2016-05-25 12:27:22 +00:00
|
|
|
def exit(self):
|
|
|
|
for gen in self.scheduled:
|
|
|
|
if gen not in self.finished and isinstance(gen, type_gen):
|
|
|
|
unschedule(gen)
|
|
|
|
unblock(gen)
|
|
|
|
gen.close()
|
|
|
|
|
2016-05-02 14:06:08 +00:00
|
|
|
def _wait(self, gen):
|
2016-05-25 12:27:22 +00:00
|
|
|
try:
|
|
|
|
if isinstance(gen, type_gen):
|
|
|
|
result = yield from gen
|
|
|
|
else:
|
|
|
|
result = yield gen
|
|
|
|
except Exception as exc:
|
|
|
|
self._finish(gen, exc)
|
2016-05-17 13:16:59 +00:00
|
|
|
else:
|
2016-05-25 12:27:22 +00:00
|
|
|
self._finish(gen, result)
|
2016-04-29 23:20:57 +00:00
|
|
|
|
2016-05-16 15:10:12 +00:00
|
|
|
def _finish(self, gen, result):
|
2016-05-25 12:27:22 +00:00
|
|
|
self.finished.append(gen)
|
|
|
|
if self.wait_for == len(self.finished) or isinstance(result, Exception):
|
2016-05-02 14:06:08 +00:00
|
|
|
if self.exit_others:
|
2016-05-25 12:27:22 +00:00
|
|
|
self.exit()
|
|
|
|
schedule(self.callback, result)
|
|
|
|
self.callback = None
|
2016-04-29 19:48:59 +00:00
|
|
|
|
2016-05-02 14:22:22 +00:00
|
|
|
|
2016-06-17 18:52:36 +00:00
|
|
|
def step_task(gen, data):
|
|
|
|
if isinstance(data, Exception):
|
|
|
|
result = gen.throw(data)
|
|
|
|
else:
|
|
|
|
result = gen.send(data)
|
|
|
|
if isinstance(result, Syscall):
|
|
|
|
result.register(gen) # Execute the syscall
|
|
|
|
elif result is None:
|
|
|
|
schedule(gen) # Just call us asap
|
|
|
|
else:
|
|
|
|
raise Exception('Unhandled result %s by %s' % (result, gen))
|
|
|
|
|
|
|
|
|
2016-05-12 14:18:40 +00:00
|
|
|
def run_forever():
|
2016-04-30 00:00:40 +00:00
|
|
|
if __debug__:
|
2016-05-12 14:18:40 +00:00
|
|
|
global log_delay_pos, log_delay_rb, log_delay_rb_len
|
2016-04-29 19:19:25 +00:00
|
|
|
|
2016-05-12 14:18:40 +00:00
|
|
|
DELAY_MAX = const(1000000)
|
2016-04-29 19:48:59 +00:00
|
|
|
|
2016-04-29 19:19:25 +00:00
|
|
|
while True:
|
|
|
|
|
2016-05-12 14:18:40 +00:00
|
|
|
# Peek at how long we can sleep while waiting for an event
|
2016-04-29 19:19:25 +00:00
|
|
|
if time_queue:
|
2016-05-11 12:39:57 +00:00
|
|
|
t, _, _, _ = time_queue[0]
|
2016-04-29 19:19:25 +00:00
|
|
|
delay = t - utime.ticks_us()
|
|
|
|
else:
|
2016-05-12 14:18:40 +00:00
|
|
|
delay = DELAY_MAX
|
2016-04-29 19:19:25 +00:00
|
|
|
|
2016-04-30 00:00:40 +00:00
|
|
|
if __debug__:
|
|
|
|
# Adding current delay to ring buffer for performance stats
|
|
|
|
log_delay_rb[log_delay_pos] = delay
|
|
|
|
log_delay_pos = (log_delay_pos + 1) % log_delay_rb_len
|
|
|
|
|
2016-06-17 18:52:36 +00:00
|
|
|
m = msg.select(delay)
|
|
|
|
if m:
|
|
|
|
# Run interrupt handlers right away, they have priority
|
|
|
|
iface, *data = m
|
|
|
|
tasks = msg_handlers.pop(iface, None)
|
|
|
|
if not tasks:
|
2016-05-30 14:15:34 +00:00
|
|
|
log.info(__name__, 'No handler for message: %s', iface)
|
2016-05-03 16:35:11 +00:00
|
|
|
continue
|
2016-04-29 19:19:25 +00:00
|
|
|
else:
|
2016-05-12 14:18:40 +00:00
|
|
|
# Run something from the time queue
|
2016-05-02 14:22:22 +00:00
|
|
|
if time_queue:
|
2016-05-12 14:18:40 +00:00
|
|
|
_, _, gen, data = heappop(time_queue)
|
2016-06-17 18:52:36 +00:00
|
|
|
tasks = (gen,)
|
2016-05-02 14:22:22 +00:00
|
|
|
else:
|
|
|
|
continue
|
2016-04-29 19:19:25 +00:00
|
|
|
|
2016-06-17 18:52:36 +00:00
|
|
|
# Run the tasks
|
|
|
|
for gen in tasks:
|
|
|
|
try:
|
|
|
|
step_task(gen, data)
|
|
|
|
except StopIteration as e:
|
|
|
|
log.debug(__name__, '%s finished', gen)
|
|
|
|
except Exception as e:
|
|
|
|
log.exception(__name__, e)
|