Skip to content

Commit

Permalink
Parallelize networking subsystem
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Aug 18, 2019
1 parent c31d436 commit 22ed127
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 116 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Expand Up @@ -127,6 +127,7 @@ Development
- Don't run "mpy-cross-setup" on each invocation of "mpy-compile"
- Don't clobber "mpy_cross_all.py"
- Improve LED signalling
- Parallelize networking subsystem


2019-06-22 0.5.1
Expand Down
13 changes: 8 additions & 5 deletions terkin/datalogger.py
Expand Up @@ -137,13 +137,16 @@ def start(self):
# Hello world.
self.device.print_bootscreen()

# Bootstrap infrastructure.
self.device.start_networking()
# Start networking and telemetry subsystems.

# Conditionally start network services and telemetry if networking is available.
if self.device.status.networking:
self.device.start_telemetry()
self.device.start_network_services()
try:
self.device.start_networking()
except Exception:
log.exception('Networking subsystem failed')
self.status.networking = False

self.device.start_telemetry()

# Todo: Signal readyness by publishing information about the device (Microhomie).
# e.g. ``self.device.publish_properties()``
Expand Down
121 changes: 56 additions & 65 deletions terkin/device.py
Expand Up @@ -60,15 +60,20 @@ def start_networking(self):
try:
self.networking.start_wifi()

# Wait for network interface to come up.
self.networking.wait_for_nic()
except Exception:
log.error('Network connectivity not available, WiFi failed')
self.status.networking = False

# Wait for network stack to come up.
try:
self.networking.wait_for_ip_stack(timeout=10)
self.status.networking = True

except WiFiException:
log.error('Network connectivity not available, WiFi failed')
self.networking.start_services()
except:
log.error('IP stack not available')
self.status.networking = False


# Initialize LoRa device.
if self.settings.get('networking.lora.antenna_attached'):
try:
Expand Down Expand Up @@ -122,52 +127,8 @@ def configure_rgb_led(self):
pycom.heartbeat(rgb_led_heartbeat)
pycom.heartbeat_on_boot(rgb_led_heartbeat)

def power_off_lte_modem(self):
"""
We don't use LTE yet.
https://community.hiveeyes.org/t/lte-modem-des-pycom-fipy-komplett-stilllegen/2161
https://forum.pycom.io/topic/4877/deepsleep-on-batteries/10
"""

def blink_led(self, color, count=1):
import pycom

"""
if not pycom.lte_modem_en_on_boot():
log.info('Skip turning off LTE modem')
return
"""

log.info('Turning off LTE modem')
try:
from network import LTE

# Invoking this will cause `LTE.deinit()` to take around 6(!) seconds.
#log.info('Enabling LTE modem on boot')
#pycom.lte_modem_en_on_boot(True)

log.info('Turning off LTE modem on boot')
pycom.lte_modem_en_on_boot(False)

log.info('Invoking LTE.deinit()')
lte = LTE()
lte.deinit()

except:
log.exception('Shutting down LTE modem failed')

def power_off_bluetooth(self):
"""
We don't use Bluetooth yet.
"""
log.info('Turning off Bluetooth')
try:
from network import Bluetooth
bluetooth = Bluetooth()
bluetooth.deinit()
except:
log.exception('Shutting down Bluetooth failed')
for _ in range(count):
pycom.rgbled(color)
time.sleep(0.15)
Expand Down Expand Up @@ -197,22 +158,6 @@ def start_telemetry(self):
except:
log.exception('Creating telemetry adapter failed for target: %s', telemetry_target)

def start_network_services(self):

# Start UDP server for pulling device into maintenance mode.
if self.settings.get('services.api.modeserver.enabled', False):
try:
self.networking.start_modeserver()
except:
log.exception('Starting mode server failed')

# Start HTTP server
if self.settings.get('services.api.http.enabled', False):
try:
self.networking.start_httpserver()
except:
log.exception('Starting HTTP server failed')

def create_telemetry_adapter(self, telemetry_target):
# Create adapter object.
telemetry_adapter = TelemetryAdapter(
Expand Down Expand Up @@ -298,6 +243,52 @@ def add(item=''):
def power_off(self):
self.networking.stop()

def power_off_lte_modem(self):
"""
We don't use LTE yet.
https://community.hiveeyes.org/t/lte-modem-des-pycom-fipy-komplett-stilllegen/2161
https://forum.pycom.io/topic/4877/deepsleep-on-batteries/10
"""

import pycom

"""
if not pycom.lte_modem_en_on_boot():
log.info('Skip turning off LTE modem')
return
"""

log.info('Turning off LTE modem')
try:
from network import LTE

# Invoking this will cause `LTE.deinit()` to take around 6(!) seconds.
#log.info('Enabling LTE modem on boot')
#pycom.lte_modem_en_on_boot(True)

log.info('Turning off LTE modem on boot')
pycom.lte_modem_en_on_boot(False)

log.info('Invoking LTE.deinit()')
lte = LTE()
lte.deinit()

except:
log.exception('Shutting down LTE modem failed')

def power_off_bluetooth(self):
"""
We don't use Bluetooth yet.
"""
log.info('Turning off Bluetooth')
try:
from network import Bluetooth
bluetooth = Bluetooth()
bluetooth.deinit()
except:
log.exception('Shutting down Bluetooth failed')

def hibernate(self, interval, deepsleep=False):

#logging.enable_logging()
Expand Down
86 changes: 75 additions & 11 deletions terkin/network/core.py
Expand Up @@ -2,6 +2,7 @@
# (c) 2019 Richard Pobering <richard@hiveeyes.org>
# (c) 2019 Andreas Motl <andreas@hiveeyes.org>
# License: GNU General Public License, Version 3
import sys
import time
import socket
import machine
Expand All @@ -10,7 +11,7 @@
from terkin.network.ip import UdpServer
from terkin.network.lora import LoRaManager
from terkin.network.wifi import WiFiManager
from terkin.util import format_exception
from terkin.util import format_exception, Eggtimer

log = logging.getLogger(__name__)

Expand All @@ -37,26 +38,85 @@ def start_wifi(self):
def start_lora(self):
self.lora_manager.start()

def wait_for_nic(self, retries=5):
attempts = 0
while attempts < retries:
def wait_for_ip_stack(self, timeout=5):

eggtimer = Eggtimer(duration=timeout)

log.info('Waiting for network stack')
while not eggtimer.expired():

self.device.watchdog.feed()

try:
socket.getaddrinfo("localhost", 333)
break
log.info('Network stack ready')
return True

except OSError as ex:
log.warning('Network stack not available', format_exception(ex))

# Report about progress.
sys.stderr.write('.')
#sys.stderr.flush()

# Save power while waiting.
machine.idle()
time.sleep(0.25)

# TODO: Make WiFi-agnostic.
raise NetworkUnavailable('Could not connect to WiFi network')

def wait_for_nic(self, timeout=5):

eggtimer = Eggtimer(duration=timeout)

log.info('Waiting for network interface')
while not eggtimer.expired():

self.device.watchdog.feed()

try:
# TODO: Make WiFi-agnostic.
if self.wifi_manager.is_connected():
log.info('Network interface ready')
return True

except OSError as ex:
log.warning('Network interface not available: %s', format_exception(ex))
log.info('Waiting for network interface')

# Report about progress.
sys.stderr.write('.')
#sys.stderr.flush()

# Save power while waiting.
machine.idle()
time.sleep(0.25)
attempts += 1
log.info('Network interface ready')

# TODO: Make WiFi-agnostic.
raise NetworkUnavailable('Could not connect to WiFi network')

def start_services(self):

# Start UDP server for pulling device into maintenance mode.
if self.settings.get('services.api.modeserver.enabled', False):
try:
self.start_modeserver()
except:
log.exception('Starting mode server failed')

# Start HTTP server
if self.settings.get('services.api.http.enabled', False):
try:
self.start_httpserver()
except:
log.exception('Starting HTTP server failed')

def start_modeserver(self):
"""
Start UDP server for pulling device into maintenance mode.
"""
ip = self.wifi_manager.get_ip_address()
#ip = self.wifi_manager.get_ip_address()
ip = '0.0.0.0'
port = 666
log.info('Starting mode server on {}:{}'.format(ip, port))
self.mode_server = UdpServer(ip, port)
Expand All @@ -74,12 +134,16 @@ def handle_modeserver(self, data, addr):

message = data.decode()

if message == 'maintenance.enable()':
if message == 'maintenance.enable()' and not self.device.status.maintenance:
log.info('Enabling maintenance mode')
self.device.status.maintenance = True
self.device.watchdog.suspend()

elif message == 'maintenance.disable()':
elif message == 'maintenance.disable()' and self.device.status.maintenance:
log.info('Releasing maintenance mode')
self.device.status.maintenance = False
self.device.watchdog.resume()


class NetworkUnavailable(Exception):
pass

0 comments on commit 22ed127

Please sign in to comment.