2016-04-28 05:43:37 +00:00
|
|
|
import utime
|
2016-09-29 10:29:43 +00:00
|
|
|
from micropython import const
|
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-10-04 14:33:38 +00:00
|
|
|
from . import ui
|
2016-04-28 05:43:37 +00:00
|
|
|
|
2016-04-29 19:48:59 +00:00
|
|
|
if __debug__:
|
2016-08-05 10:32:37 +00:00
|
|
|
# for performance stats
|
2016-04-29 19:48:59 +00:00
|
|
|
import array
|
|
|
|
log_delay_pos = 0
|
|
|
|
log_delay_rb_len = const(10)
|
|
|
|
log_delay_rb = array.array('i', [0] * log_delay_rb_len)
|
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
paused_tasks = {} # {message interface: [task]}
|
|
|
|
schedule_counter = 0
|
|
|
|
scheduled_tasks = [] # heap: [(time, counter, task, value)]
|
|
|
|
MAX_SELECT_DELAY = const(1000000)
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
# message interfaces:
|
|
|
|
# 0-255 - USB HID
|
|
|
|
# 256 - touch event interface
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
TOUCH = const(256) # interface
|
|
|
|
TOUCH_START = const(1) # event
|
|
|
|
TOUCH_MOVE = const(2) # event
|
|
|
|
TOUCH_END = const(4) # event
|
2016-05-02 14:22:22 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
|
|
|
|
def schedule_task(task, value=None, time=None):
|
|
|
|
global schedule_counter
|
|
|
|
if time is None:
|
2016-04-29 19:48:59 +00:00
|
|
|
time = utime.ticks_us()
|
2016-08-05 10:32:37 +00:00
|
|
|
heappush(scheduled_tasks, (time, schedule_counter, task, value))
|
|
|
|
schedule_counter += 1
|
|
|
|
|
|
|
|
|
|
|
|
def unschedule_task(task):
|
|
|
|
global scheduled_tasks
|
|
|
|
scheduled_tasks = [t for t in scheduled_tasks if t[1] is not task]
|
|
|
|
heapify(scheduled_tasks)
|
|
|
|
|
2016-05-16 15:10:12 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def pause_task(task, iface):
|
|
|
|
paused_tasks.setdefault(iface, []).append(task)
|
2016-05-16 15:10:12 +00:00
|
|
|
|
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def unpause_task(task):
|
|
|
|
for iface in paused_tasks:
|
|
|
|
if task in paused_tasks[iface]:
|
|
|
|
paused_tasks[iface].remove(task)
|
2016-05-16 15:10:12 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
|
|
|
|
def run_task(task, value):
|
|
|
|
try:
|
|
|
|
if isinstance(value, Exception):
|
|
|
|
result = task.throw(value)
|
|
|
|
else:
|
|
|
|
result = task.send(value)
|
|
|
|
except StopIteration as e:
|
|
|
|
log.debug(__name__, '%s finished', task)
|
|
|
|
except Exception as e:
|
|
|
|
log.exception(__name__, e)
|
2016-06-17 18:52:36 +00:00
|
|
|
else:
|
2016-08-05 10:32:37 +00:00
|
|
|
if isinstance(result, Syscall):
|
|
|
|
result.handle(task)
|
|
|
|
elif result is None:
|
|
|
|
schedule_task(task)
|
|
|
|
else:
|
|
|
|
log.error(__name__, '%s is unknown syscall', result)
|
2016-05-16 15:10:12 +00:00
|
|
|
|
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def handle_message(message):
|
|
|
|
if not paused_tasks:
|
|
|
|
return
|
|
|
|
iface, *value = message
|
|
|
|
tasks = paused_tasks.pop(iface, ())
|
|
|
|
for task in tasks:
|
|
|
|
run_task(task, value)
|
|
|
|
|
|
|
|
|
|
|
|
def handle_timeout():
|
|
|
|
if not scheduled_tasks:
|
|
|
|
return
|
|
|
|
_, _, task, value = heappop(scheduled_tasks)
|
|
|
|
run_task(task, value)
|
|
|
|
|
|
|
|
|
|
|
|
def run_forever():
|
|
|
|
if __debug__:
|
|
|
|
global log_delay_pos
|
|
|
|
while True:
|
|
|
|
if scheduled_tasks:
|
|
|
|
t, _, _, _ = scheduled_tasks[0]
|
|
|
|
delay = t - utime.ticks_us()
|
|
|
|
else:
|
|
|
|
delay = MAX_SELECT_DELAY
|
|
|
|
if __debug__:
|
|
|
|
# add 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-10-04 14:33:38 +00:00
|
|
|
ui.display.refresh()
|
2016-08-05 10:32:37 +00:00
|
|
|
message = msg.select(delay)
|
|
|
|
if message:
|
|
|
|
handle_message(message)
|
|
|
|
else:
|
|
|
|
handle_timeout()
|
2016-05-12 14:18:40 +00:00
|
|
|
|
|
|
|
|
2016-05-30 14:15:34 +00:00
|
|
|
class Syscall():
|
2016-08-05 10:32:37 +00:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return (yield self)
|
2016-05-30 14:15:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Sleep(Syscall):
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def __init__(self, delay_us):
|
|
|
|
self.time = delay_us + utime.ticks_us()
|
2016-05-12 14:18:40 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def handle(self, task):
|
|
|
|
schedule_task(task, self, self.time)
|
2016-05-30 14:15:34 +00:00
|
|
|
|
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-08-05 10:32:37 +00:00
|
|
|
def handle(self, task):
|
|
|
|
pause_task(task, self.iface)
|
|
|
|
|
|
|
|
|
|
|
|
NO_VALUE = ()
|
|
|
|
|
|
|
|
|
|
|
|
class Future(Syscall):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.value = NO_VALUE
|
|
|
|
self.task = None
|
|
|
|
|
|
|
|
def handle(self, task):
|
|
|
|
self.task = task
|
|
|
|
if self.value is not NO_VALUE:
|
|
|
|
self._deliver()
|
|
|
|
|
|
|
|
def resolve(self, value):
|
|
|
|
if self.value is NO_VALUE:
|
|
|
|
self.value = value
|
|
|
|
if self.task is not None:
|
|
|
|
self._deliver()
|
|
|
|
|
|
|
|
def _deliver(self):
|
|
|
|
schedule_task(self.task, self.value)
|
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-08-05 10:32:37 +00:00
|
|
|
def __init__(self, children, wait_for=1, exit_others=True):
|
|
|
|
self.children = children
|
2016-05-02 14:06:08 +00:00
|
|
|
self.wait_for = wait_for
|
|
|
|
self.exit_others = exit_others
|
2016-08-05 10:32:37 +00:00
|
|
|
self.scheduled = []
|
2016-05-25 12:27:22 +00:00
|
|
|
self.finished = []
|
2016-05-02 14:06:08 +00:00
|
|
|
self.callback = None
|
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def handle(self, task):
|
|
|
|
self.callback = task
|
|
|
|
self.scheduled = [self._wait(c) for c in self.children]
|
|
|
|
for ct in self.scheduled:
|
|
|
|
schedule_task(ct)
|
2016-05-02 14:06:08 +00:00
|
|
|
|
2016-05-25 12:27:22 +00:00
|
|
|
def exit(self):
|
2016-08-05 10:32:37 +00:00
|
|
|
for task in self.scheduled:
|
|
|
|
if task not in self.finished:
|
|
|
|
unschedule_task(task)
|
|
|
|
unpause_task(task)
|
|
|
|
task.close()
|
2016-05-25 12:27:22 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def _wait(self, child):
|
2016-05-25 12:27:22 +00:00
|
|
|
try:
|
2016-08-05 10:32:37 +00:00
|
|
|
if isinstance(child, type_gen):
|
|
|
|
result = yield from child
|
2016-05-25 12:27:22 +00:00
|
|
|
else:
|
2016-08-05 10:32:37 +00:00
|
|
|
result = yield child
|
|
|
|
except Exception as e:
|
|
|
|
self._finish(child, e)
|
2016-05-17 13:16:59 +00:00
|
|
|
else:
|
2016-08-05 10:32:37 +00:00
|
|
|
self._finish(child, result)
|
2016-04-29 23:20:57 +00:00
|
|
|
|
2016-08-05 10:32:37 +00:00
|
|
|
def _finish(self, child, result):
|
|
|
|
self.finished.append(child)
|
2016-05-25 12:27:22 +00:00
|
|
|
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()
|
2016-08-05 10:32:37 +00:00
|
|
|
schedule_task(self.callback, result)
|
2016-09-25 13:55:08 +00:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
try:
|
|
|
|
return (yield self)
|
|
|
|
except:
|
|
|
|
self.exit()
|
|
|
|
raise
|