Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python application

on:
push:
branches: [ python3 ]
pull_request:
branches: [ python3 ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f tests-requirements.txt ]; then pip install -r tests-requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 openvisualizer --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 openvisualizer --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest tests/ov
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# cache files
*.pyc
.sconsign.dblite

# folders
venv/
venv
openvisualizer.egg-info

# IDE files
Expand Down
3 changes: 1 addition & 2 deletions openvisualizer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
VERSION = '2.0.0'
VERSION = '2.1.0'

PACKAGE_NAME = 'openvisualizer'
APPNAME = PACKAGE_NAME


# cannot use os.path.join according to pkg_resources
DEFAULT_LOGGING_CONF = '/'.join(("config", "logging.conf"))
WINDOWS_COLORS = '/'.join(('config', 'colors_win.conf'))
Expand Down
249 changes: 247 additions & 2 deletions openvisualizer/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,249 @@
from openvisualizer.main import main
import logging
import logging.config
import os
import signal
import sys
from collections import namedtuple
from configparser import SafeConfigParser
from typing import Optional
from xmlrpc.server import SimpleXMLRPCServer

import appdirs
import click
import coloredlogs
import pkg_resources as pkg_rs

from openvisualizer import APPNAME, PACKAGE_NAME, DEFAULT_LOGGING_CONF, WINDOWS_COLORS, UNIX_COLORS, VERSION
from openvisualizer.server import OpenVisualizer

server_object: Optional[OpenVisualizer] = None


def sigint_handler(sig, frame):
if server_object is not None:
server_object.shutdown()


signal.signal(signal.SIGINT, sigint_handler)

log = logging.getLogger('Main')

ServerConfig = namedtuple('ServerConfig',
[
'host',
'port',
'wireshark_debug',
'tun',
'lconf',
'page_zero',
'mqtt_broker',
'root'
])

pass_config = click.make_pass_decorator(ServerConfig, ensure=True)


class ColoredFormatter(coloredlogs.ColoredFormatter):
""" Class that matches coloredlogs.ColoredFormatter arguments with logging.Formatter """

def __init__(self, fmt=None, datefmt=None, style=None):
self.parser = SafeConfigParser()

if sys.platform.startswith('win32'):
log_colors_conf = pkg_rs.resource_filename(PACKAGE_NAME, WINDOWS_COLORS)
else:
log_colors_conf = pkg_rs.resource_filename(PACKAGE_NAME, UNIX_COLORS)

self.parser.read(log_colors_conf)

ls = self.parse_section('levels', 'keys')
fs = self.parse_section('fields', 'keys')

coloredlogs.ColoredFormatter.__init__(self, fmt=fmt, datefmt=datefmt, level_styles=ls, field_styles=fs)

def parse_section(self, section, option):
dictionary = {}

if not self.parser.has_section(section) or not self.parser.has_option(section, option):
log.warning('Unknown section {} or option {}'.format(section, option))
return dictionary

subsections = map(str.strip, self.parser.get(section, option).split(','))

for subsection in subsections:
if not self.parser.has_section(str(subsection)):
log.warning('Unknown section name: {}'.format(subsection))
continue

dictionary[subsection] = {}
options = self.parser.options(subsection)

for opt in options:
res = self.parse_options(subsection, opt.strip().lower())
if res is not None:
dictionary[subsection][opt] = res

return dictionary

def parse_options(self, section, option):
res = None
if option == 'bold' or option == 'faint':
try:
return self.parser.getboolean(section, option)
except ValueError:
log.error('Illegal value: {} for option: {}'.format(self.parser.get(section, option), option))
elif option == 'color':
try:
res = self.parser.getint(section, option)
except ValueError:
res = self.parser.get(section, option)
else:
log.warning('Unknown option name: {}'.format(option))

return res


@click.group(invoke_without_command=True)
@click.option('--host', default='localhost', help='Specify address of the OpenVisualizer server', show_default=True)
@click.option('--port', default=9000, help='Specify to port to use', show_default=True)
@click.option('--version', '-v', is_flag=True, help='Print version information OpenVisualizer')
@click.option('--tun', is_flag=True, help="Enable the TUN interface")
@click.option('--wireshark-debug', '-w', is_flag=True, help="Enable wireshark debugging")
@click.option('--lconf', default=pkg_rs.resource_filename(PACKAGE_NAME, DEFAULT_LOGGING_CONF),
help="Provide a logging configuration")
@click.option('--page-zero', is_flag=True, help="Uses page number 0 in page dispatch (only works with single hop)")
@click.option('--mqtt-broker', default=None, type=str, help='Specify address MQTT server for network stats.')
@click.option('--root', '-r', type=str, help='Mark a mote as DAGroot, e.g. /dev/ttyUSB* or COM*')
@click.pass_context
def cli(ctx, host, port, version, wireshark_debug, tun, lconf, page_zero, mqtt_broker, root):
banner = [""]
banner += [" ___ _ _ _ ___ _ _ "]
banner += ["| . | ___ ___ ._ _ | | | |/ __>| \\ |"]
banner += ["| | || . \\/ ._>| ' || | | |\\__ \\| |"]
banner += ["`___'| _/\\___.|_|_||__/_/ <___/|_\\_|"]
banner += [" |_| openwsn.org"]
banner += [""]

click.secho('\n'.join(banner))

if version:
click.echo(f"OpenVisualizer (server) v{VERSION}")
sys.exit(0)

if ctx.invoked_subcommand is None:
click.echo('Use one of the following subcommands: ', nl=False)
click.secho('hardware, simulation, iotlab, or testbed', bold=True)
sys.exit(0)

if tun and os.name == 'posix' and not os.getuid() == 0:
res = click.prompt("TUN requires admin privileges, (C)ontinue without privileges and with TUN or (A)bort",
default="A")
if res != "C" and res != 'c':
sys.exit(0)

# create directories to store logs and application data
try:
os.makedirs(appdirs.user_log_dir(APPNAME))
except OSError as err:
if err.errno != 17:
log.critical(err)
return

try:
os.makedirs(appdirs.user_data_dir(APPNAME))
except OSError as err:
if err.errno != 17:
log.critical(err)
return

ctx.obj = ServerConfig(host, port, wireshark_debug, tun, lconf, page_zero, mqtt_broker, root)
load_logging_conf(ctx.obj)


@click.command()
@click.option('--baudrate', '-b', default=['115200'], help='A list of baudrates to test', show_default=True)
@click.option('--port-mask', '-p', help='Define a port mask for probing hardware, e.g., /dev/ttyUSB*', type=str,
multiple=True)
@pass_config
def hardware(config, baudrate, port_mask):
""" OpenVisualizer in hardware mode."""

if isinstance(baudrate, str):
baudrate = [baudrate]

start_server(OpenVisualizer(config, OpenVisualizer.Mode.HARDWARE, baudrate=baudrate, port_mask=port_mask), config)


@click.command()
@pass_config
@click.argument('num_of_motes', nargs=1, type=int)
@click.option('--topology', '-t', default='fully-meshed', help='Specify the simulation topology', show_default=True)
def simulation(config, num_of_motes, topology):
""" OpenVisualizer in simulation mode."""

start_server(
OpenVisualizer(config, OpenVisualizer.Mode.SIMULATION, num_of_motes=num_of_motes, topology=topology), config)


@click.command()
@pass_config
def testbed(config):
""" Attaches OpenVisualizer to the opentestbed. """

start_server(OpenVisualizer(config, OpenVisualizer.Mode.TESTBED), config)


@click.command()
@click.argument('mote_ids', nargs=-1, type=str)
@click.option('--user', '-u', help='IoTLAB username')
@click.option('--site', '-s', help='IoTLAB frontend site')
@click.option('--key-file', '-k', help='SSH private key file, e.g., ~/.ssh/id_rsa')
@click.option('--debug', '-d', help='Enable debugging output', is_flag=True)
@pass_config
def iotlab(config, mote_ids, debug, user, site, key_file):
""" Attaches OpenVisualizer to IoT-LAB."""

start_server(
OpenVisualizer(config,
OpenVisualizer.Mode.IOTLAB,
iotlab_user=user,
iotlab_site=site,
iotlab_key_file=key_file,
iotlab_motes=mote_ids,
debug=debug),
config)


def start_server(server_instance, config):
global server_object
server_object = server_instance
with SimpleXMLRPCServer((config.host, config.port), allow_none=True, logRequests=False,
bind_and_activate=False) as server:

server.allow_reuse_address = True

server.server_bind()
server.server_activate()

server.register_instance(server_instance, allow_dotted_names=False)
try:
server.serve_forever()
except KeyboardInterrupt:
pass


def load_logging_conf(config):
try:
log_dir = appdirs.user_log_dir(APPNAME)
logging.config.fileConfig(fname=config.lconf, defaults={'log_dir': log_dir})
except IOError as err:
log.error(f"Failed to load config: {err}")


cli.add_command(hardware)
cli.add_command(simulation)
cli.add_command(testbed)
cli.add_command(iotlab)

if __name__ == "__main__":
main()
cli()
50 changes: 0 additions & 50 deletions openvisualizer/bspemulator/bspboard.py

This file was deleted.

Loading