From f8bd1a74613a79cad56b5b60079402d40a87d131 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Sun, 22 Mar 2020 12:37:19 +0000 Subject: Rename manager.py and expose its interfaces to all applications This is a big change that break compatiblity with existing applications *and* with existing installed versions of main.py. When upgrading it is import to update main.py: ./tools/wasptool --upload wasp/main.py diff --git a/docs/wasp.rst b/docs/wasp.rst index 039e819..e1f5bec 100644 --- a/docs/wasp.rst +++ b/docs/wasp.rst @@ -4,7 +4,7 @@ WASP Internals System management ----------------- -.. automodule:: manager +.. automodule:: wasp :members: :undoc-members: diff --git a/tools/wasptool b/tools/wasptool index 72e88bc..e546016 100755 --- a/tools/wasptool +++ b/tools/wasptool @@ -14,6 +14,11 @@ import string import sys def sync(c): + """Stop the watch and synchronize with the command prompt. + + Sending a random print ensure the final export (of the prompt) + does not accidentally match a previously issued prompt. + """ tag = ''.join([random.choice(string.ascii_uppercase) for i in range(6)]) c.send('\x03') @@ -23,8 +28,14 @@ def sync(c): c.expect('>>> ') def unsync(c): - # Set the watch running again - c.sendline('wasp.run()') + """Set the watch running again. + + There must be an expect (or a sleep) since if we kill the subordinate + process too early then the sendline will not have completed. + """ + c.sendline('wasp.system.run()') + c.expect('Watch is running, use Ctrl-C to stop') + c.send('\x18') def paste(c, f, verbose=False): docstring = False diff --git a/wasp/apps/clock.py b/wasp/apps/clock.py index f99091a..55295aa 100644 --- a/wasp/apps/clock.py +++ b/wasp/apps/clock.py @@ -1,7 +1,6 @@ +import wasp + import fonts.clock as digits -import watch -import widgets -import manager DIGITS = ( digits.clock_0, @@ -18,28 +17,28 @@ DIGITS = ( MONTH = 'JanFebMarAprMayJunJulAugSepOctNovDec' -class ClockApp(object): +class ClockApp(): """Simple digital clock application. Shows a time (as HH:MM) together with a battery meter and the date. """ def __init__(self): - self.meter = widgets.BatteryMeter() + self.meter = wasp.widgets.BatteryMeter() def handle_event(self, event_view): """Process events that the app is subscribed to.""" - if event_view[0] == manager.EVENT_TICK: + if event_view[0] == wasp.EVENT_TICK: self.update() else: # TODO: Raise an unexpected event exception pass - def foreground(self, manager, effect=None): + def foreground(self, effect=None): """Activate the application.""" self.on_screen = ( -1, -1, -1, -1, -1, -1 ) self.draw(effect) - manager.request_tick(1000) + wasp.system.request_tick(1000) def tick(self, ticks): self.update() @@ -56,7 +55,7 @@ class ClockApp(object): def draw(self, effect=None): """Redraw the display from scratch.""" - draw = watch.drawable + draw = wasp.watch.drawable draw.fill() draw.rleblit(digits.clock_colon, pos=(2*48, 80), fg=0xb5b6) @@ -70,14 +69,14 @@ class ClockApp(object): The updates are a lazy as possible and rely on an prior call to draw() to ensure the screen is suitably prepared. """ - now = watch.rtc.get_localtime() + now = wasp.watch.rtc.get_localtime() if now[3] == self.on_screen[3] and now[4] == self.on_screen[4]: if now[5] != self.on_screen[5]: self.meter.update() self.on_screen = now return False - draw = watch.drawable + draw = wasp.watch.drawable draw.rleblit(DIGITS[now[4] % 10], pos=(4*48, 80)) draw.rleblit(DIGITS[now[4] // 10], pos=(3*48, 80), fg=0xbdb6) draw.rleblit(DIGITS[now[3] % 10], pos=(1*48, 80)) diff --git a/wasp/apps/flashlight.py b/wasp/apps/flashlight.py index f1f9418..13341b0 100644 --- a/wasp/apps/flashlight.py +++ b/wasp/apps/flashlight.py @@ -1,5 +1,4 @@ -import watch -import manager +import wasp class FlashlightApp(object): """Trivial flashlight application. @@ -10,11 +9,11 @@ class FlashlightApp(object): def __init__(self): self.backlight = None - def foreground(self, manager, effect=None): + def foreground(self, effect=None): """Activate the application.""" self.on_screen = ( -1, -1, -1, -1, -1, -1 ) self.draw(effect) - manager.request_tick(1000) + wasp.system.request_tick(1000) def background(self): """De-activate the application (without losing state).""" @@ -28,5 +27,5 @@ class FlashlightApp(object): def draw(self, effect=None): """Redraw the display from scratch.""" - display = watch.display + display = wasp.watch.display display.fill(0xffff) diff --git a/wasp/apps/testapp.py b/wasp/apps/testapp.py index 0ab33a3..fc32ea0 100644 --- a/wasp/apps/testapp.py +++ b/wasp/apps/testapp.py @@ -1,7 +1,5 @@ -import watch -import widgets -import manager import machine +import wasp class TestApp(): """Simple test application. @@ -11,11 +9,11 @@ class TestApp(): self.tests = ('Touch', 'String') self.test = self.tests[0] - def foreground(self, system, effect=None): + def foreground(self, effect=None): """Activate the application.""" self.on_screen = ( -1, -1, -1, -1, -1, -1 ) self.draw(effect) - system.request_event(manager.EVENT_TOUCH | manager.EVENT_SWIPE_UPDOWN) + wasp.system.request_event(wasp.EVENT_TOUCH | wasp.EVENT_SWIPE_UPDOWN) def background(self): """De-activate the application (without losing state).""" @@ -33,7 +31,7 @@ class TestApp(): self.draw() def touch(self, event): - draw = watch.drawable + draw = wasp.watch.drawable if self.test == 'Touch': draw.string('({}, {})'.format(event[1], event[2]), 0, 108, width=240) @@ -55,8 +53,8 @@ class TestApp(): def draw(self, effect=None): """Redraw the display from scratch.""" - watch.display.mute(True) - watch.drawable.fill() - watch.drawable.string('{} test'.format(self.test), + wasp.watch.display.mute(True) + wasp.watch.drawable.fill() + wasp.watch.drawable.string('{} test'.format(self.test), 0, 6, width=240) - watch.display.mute(False) + wasp.watch.display.mute(False) diff --git a/wasp/boards/pinetime/manifest.py b/wasp/boards/pinetime/manifest.py index 6a19c1a..cdf2e15 100644 --- a/wasp/boards/pinetime/manifest.py +++ b/wasp/boards/pinetime/manifest.py @@ -17,8 +17,8 @@ freeze('../..', 'fonts/sans24.py', 'icons.py', 'logo.py', - 'manager.py', 'shell.py', + 'wasp.py', 'widgets.py', ), opt=3 diff --git a/wasp/main.py b/wasp/main.py index cc4c8b6..4f1de10 100644 --- a/wasp/main.py +++ b/wasp/main.py @@ -1,3 +1,2 @@ -import manager, watch -wasp = manager.Manager(watch) -wasp.run() +import wasp +wasp.system.run() diff --git a/wasp/manager.py b/wasp/manager.py deleted file mode 100644 index 055b7d1..0000000 --- a/wasp/manager.py +++ /dev/null @@ -1,150 +0,0 @@ -import gc -import machine - -from apps.clock import ClockApp -from apps.flashlight import FlashlightApp -from apps.testapp import TestApp - -DOWN = 1 -UP = 2 -LEFT = 3 -RIGHT = 4 - -EVENT_TOUCH = 0x0001 -EVENT_SWIPE_LEFTRIGHT = 0x0002 -EVENT_SWIPE_UPDOWN = 0x0004 -EVENT_BUTTON = 0x0008 - -class Manager(object): - def __init__(self, watch): - self.watch = watch - - self.app = None - - self.applications = [ - ClockApp(), - FlashlightApp(), - TestApp() - ] - - self.watch.display.poweron() - self.switch(self.applications[0]) - self.watch.backlight.set(2) - - self.sleep_at = watch.rtc.uptime + 90 - self.charging = True - - def switch(self, app): - if self.app: - self.app.background() - - # Clear out any configuration from the old application - self.event_mask = 0 - self.tick_period_ms = 0 - self.tick_expiry = None - - self.app = app - self.watch.display.mute(True) - app.foreground(self) - self.watch.display.mute(False) - - def navigate(self, direction=None): - """Navigate between different applications. - - Currently the direction is ignored. - """ - app_list = self.applications - - if direction == LEFT: - i = app_list.index(self.app) + 1 - if i >= len(app_list): - i = 0 - self.switch(app_list[i]) - elif direction == RIGHT: - i = app_list.index(self.app) - 1 - if i < 0: - i = len(app_list)-1 - self.switch(app_list[i]) - - def request_event(self, event_mask): - self.event_mask |= event_mask - - def request_tick(self, period_ms=None): - """Request (and subscribe to) a periodic tick event. - - Note: With the current simplistic timer implementation sub-second - tick intervals are not possible. - """ - self.tick_period_ms = period_ms - self.tick_expiry = self.watch.rtc.get_uptime_ms() + period_ms - - def handle_event(self, event): - self.sleep_at = self.watch.rtc.uptime + 15 - - event_mask = self.event_mask - if event[0] < 5: - updown = event[0] == 1 or event[0] == 2 - if (bool(event_mask & EVENT_SWIPE_UPDOWN) and updown) or \ - (bool(event_mask & EVENT_SWIPE_LEFTRIGHT) and not updown): - if not self.app.swipe(event): - self.navigate(event[0]) - else: - self.navigate(event[0]) - elif event[0] == 5 and self.event_mask & EVENT_TOUCH: - self.app.touch(event) - - def tick(self): - rtc = self.watch.rtc - - if self.sleep_at: - if rtc.update() and self.tick_expiry: - now = rtc.get_uptime_ms() - - if self.tick_expiry <= now: - ticks = 0 - while self.tick_expiry <= now: - self.tick_expiry += self.tick_period_ms - ticks += 1 - self.app.tick(ticks) - - if self.watch.button.value(): - self.sleep_at = self.watch.rtc.uptime + 15 - - event = self.watch.touch.get_event() - if event: - self.handle_event(event) - - if self.watch.rtc.uptime > self.sleep_at: - self.watch.backlight.set(0) - if not self.app.sleep(): - self.switch(self.applications[0]) - self.app.sleep() - self.watch.display.poweroff() - self.charging = self.watch.battery.charging() - self.sleep_at = None - - gc.collect() - else: - self.watch.rtc.update() - - charging = self.watch.battery.charging() - if self.watch.button.value() or self.charging != charging: - self.watch.display.poweron() - self.app.wake() - self.watch.backlight.set(2) - - # Discard any pending touch events - _ = self.watch.touch.get_event() - - self.sleep_at = self.watch.rtc.uptime + 15 - - def run(self): - """Run the system manager synchronously. - - This allows all watch management activities to handle in the - normal execution context meaning any exceptions and other problems - can be observed interactively via the console. - """ - while True: - self.tick() - machine.deepsleep() diff --git a/wasp/wasp.py b/wasp/wasp.py new file mode 100644 index 0000000..81a8589 --- /dev/null +++ b/wasp/wasp.py @@ -0,0 +1,157 @@ +import gc +import machine +import watch +import widgets + +from apps.clock import ClockApp +from apps.flashlight import FlashlightApp +from apps.testapp import TestApp + +DOWN = 1 +UP = 2 +LEFT = 3 +RIGHT = 4 + +EVENT_TOUCH = 0x0001 +EVENT_SWIPE_LEFTRIGHT = 0x0002 +EVENT_SWIPE_UPDOWN = 0x0004 +EVENT_BUTTON = 0x0008 + +class Manager(object): + def __init__(self): + self.app = None + + self.applications = [ + ClockApp(), + FlashlightApp(), + TestApp() + ] + self.charging = True + + def switch(self, app): + if self.app: + self.app.background() + else: + # System start up... + watch.display.poweron() + watch.display.mute(True) + watch.backlight.set(2) + self.sleep_at = watch.rtc.uptime + 90 + + # Clear out any configuration from the old application + self.event_mask = 0 + self.tick_period_ms = 0 + self.tick_expiry = None + + self.app = app + watch.display.mute(True) + app.foreground() + watch.display.mute(False) + + def navigate(self, direction=None): + """Navigate between different applications. + + Currently the direction is ignored. + """ + app_list = self.applications + + if direction == LEFT: + i = app_list.index(self.app) + 1 + if i >= len(app_list): + i = 0 + self.switch(app_list[i]) + elif direction == RIGHT: + i = app_list.index(self.app) - 1 + if i < 0: + i = len(app_list)-1 + self.switch(app_list[i]) + + def request_event(self, event_mask): + self.event_mask |= event_mask + + def request_tick(self, period_ms=None): + """Request (and subscribe to) a periodic tick event. + + Note: With the current simplistic timer implementation sub-second + tick intervals are not possible. + """ + self.tick_period_ms = period_ms + self.tick_expiry = watch.rtc.get_uptime_ms() + period_ms + + def handle_event(self, event): + self.sleep_at = watch.rtc.uptime + 15 + + event_mask = self.event_mask + if event[0] < 5: + updown = event[0] == 1 or event[0] == 2 + if (bool(event_mask & EVENT_SWIPE_UPDOWN) and updown) or \ + (bool(event_mask & EVENT_SWIPE_LEFTRIGHT) and not updown): + if not self.app.swipe(event): + self.navigate(event[0]) + else: + self.navigate(event[0]) + elif event[0] == 5 and self.event_mask & EVENT_TOUCH: + self.app.touch(event) + + def tick(self): + rtc = watch.rtc + + if self.sleep_at: + if rtc.update() and self.tick_expiry: + now = rtc.get_uptime_ms() + + if self.tick_expiry <= now: + ticks = 0 + while self.tick_expiry <= now: + self.tick_expiry += self.tick_period_ms + ticks += 1 + self.app.tick(ticks) + + if watch.button.value(): + self.sleep_at = watch.rtc.uptime + 15 + + event = watch.touch.get_event() + if event: + self.handle_event(event) + + if watch.rtc.uptime > self.sleep_at: + watch.backlight.set(0) + if not self.app.sleep(): + self.switch(self.applications[0]) + self.app.sleep() + watch.display.poweroff() + self.charging = watch.battery.charging() + self.sleep_at = None + + gc.collect() + else: + watch.rtc.update() + + charging = watch.battery.charging() + if watch.button.value() or self.charging != charging: + watch.display.poweron() + self.app.wake() + watch.backlight.set(2) + + # Discard any pending touch events + _ = watch.touch.get_event() + + self.sleep_at = watch.rtc.uptime + 15 + + def run(self): + """Run the system manager synchronously. + + This allows all watch management activities to handle in the + normal execution context meaning any exceptions and other problems + can be observed interactively via the console. + """ + if not self.app: + self.switch(self.applications[0]) + + print('Watch is running, use Ctrl-C to stop') + + while True: + self.tick() + machine.deepsleep() + +system = Manager() -- cgit v0.10.2