├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Jenkinsfile ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── docs ├── bspemulator.rst ├── conf.py ├── eventbus.rst ├── index.rst ├── jrc.rst ├── moteconnector.rst ├── moteprobe.rst ├── motestate.rst ├── openlbr.rst ├── openparser.rst ├── opentun.rst ├── rpl.rst └── simengine.rst ├── images ├── openv-client-motes.png ├── openv-client-root.png ├── openv-client-views.png ├── openv-client.png ├── openv-serial.png ├── openv-tun.png ├── ov_arch.png ├── ov_arch.svg ├── web_interface_topology.png ├── webview-motes.png └── webview-topology.png ├── openvisualizer ├── __init__.py ├── __main__.py ├── bspemulator │ ├── __init__.py │ ├── bspboard.py │ ├── bspdebugpins.py │ ├── bspeui64.py │ ├── bspleds.py │ ├── bspmodule.py │ ├── bspradio.py │ ├── bspsctimer.py │ ├── bspuart.py │ ├── hwcrystal.py │ ├── hwmodule.py │ ├── hwsupply.py │ └── vcdlogger.py ├── client │ ├── __init__.py │ ├── main.py │ ├── plugins │ │ ├── __init__.py │ │ ├── macstats.py │ │ ├── motestatus.py │ │ ├── msf.py │ │ ├── neighbors.py │ │ ├── pktqueue.py │ │ ├── plugin.py │ │ └── schedule.py │ ├── utils.py │ ├── view.py │ ├── web_files │ │ ├── static │ │ │ ├── css │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.min.css │ │ │ │ ├── d3.css │ │ │ │ └── openvisualizer.css │ │ │ ├── font-awesome │ │ │ │ ├── css │ │ │ │ │ ├── font-awesome.css │ │ │ │ │ └── font-awesome.min.css │ │ │ │ └── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ └── fontawesome-webfont.woff │ │ │ ├── images │ │ │ │ ├── favicon.ico │ │ │ │ └── openwsn_logo.png │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.min.js │ │ │ │ ├── d3.min.js │ │ │ │ ├── dagre-d3.min.js │ │ │ │ ├── jiraissuecollector.js │ │ │ │ ├── jquery-1.10.2.js │ │ │ │ ├── markerwithlabel.js │ │ │ │ ├── openvisualizer.js │ │ │ │ └── plugins │ │ │ │ └── metisMenu │ │ │ │ └── jquery.metisMenu.js │ │ └── templates │ │ │ ├── connectivity.tmpl │ │ │ ├── eventBus.tmpl │ │ │ ├── head.tmpl │ │ │ ├── moteview.tmpl │ │ │ ├── navbar.tmpl │ │ │ ├── routing.tmpl │ │ │ └── topology.tmpl │ └── webserver.py ├── config │ ├── colors_unix.conf │ ├── colors_win.conf │ ├── logging.conf │ └── trace.conf ├── eventbus │ ├── __init__.py │ ├── eventbusclient.py │ └── eventbusmonitor.py ├── jrc │ ├── __init__.py │ ├── cojp_defines.py │ └── jrc.py ├── main.py ├── motehandler │ ├── __init__.py │ ├── moteconnector │ │ ├── __init__.py │ │ ├── moteconnector.py │ │ └── openparser │ │ │ ├── __init__.py │ │ │ ├── openparser.py │ │ │ ├── parser.py │ │ │ ├── parserdata.py │ │ │ ├── parserexception.py │ │ │ ├── parserlogs.py │ │ │ ├── parserpacket.py │ │ │ ├── parserprintf.py │ │ │ └── parserstatus.py │ ├── moteprobe │ │ ├── __init__.py │ │ ├── emulatedmoteprobe.py │ │ ├── iotlabmoteprobe.py │ │ ├── mockmoteprobe.py │ │ ├── moteprobe.py │ │ ├── openhdlc.py │ │ ├── serialmoteprobe.py │ │ ├── serialtester.py │ │ └── testbedmoteprobe.py │ └── motestate │ │ ├── __init__.py │ │ ├── elements.py │ │ ├── motestate.py │ │ └── opentype.py ├── openlbr │ ├── __init__.py │ ├── openlbr.py │ └── sixlowpan_frag.py ├── opentun │ ├── __init__.py │ ├── opentun.py │ ├── opentunlinux.py │ ├── opentunmacos.py │ ├── opentunnull.py │ └── opentunwindows.py ├── rpl │ ├── __init__.py │ ├── rpl.py │ ├── sourceroute.py │ └── topology.py ├── simengine │ ├── __init__.py │ ├── idmanager.py │ ├── locationmanager.py │ ├── motehandler.py │ ├── propagation.py │ ├── simengine.py │ └── timeline.py ├── topologies │ ├── 0001-mesh.json │ ├── 0002-star.json │ └── README.md └── utils.py ├── requirements.txt ├── scripts ├── __init__.py ├── ping_responder.py └── serialtester_cli.py ├── setup.py ├── tests-requirements.txt ├── tests ├── __init__.py ├── conftest.py ├── fw │ ├── __init__.py │ ├── test_fw_frag.py │ └── test_fw_udp.py ├── ov │ ├── __init__.py │ ├── test_frag.py │ ├── test_hdlc.py │ ├── test_iotlabmoteprobe.py │ ├── test_moteprobe.py │ ├── test_sourceroute.py │ └── test_utils.py └── server_client │ ├── __init__.py │ ├── test_client.py │ └── test_server.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # cache files 2 | *.pyc 3 | .sconsign.dblite 4 | 5 | # folders 6 | venv/ 7 | openvisualizer.egg-info 8 | 9 | # IDE files 10 | .idea/ 11 | *.log 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | 5 | # Install dependencies 6 | before_install: 7 | - sudo apt-get update -qq 8 | - pip install flake8 9 | - pip install pep8-naming 10 | - pip install flake8-commas 11 | install: 12 | - pip install -r requirements.txt 13 | - pip install -r tests-requirements.txt 14 | - pip install . 15 | 16 | # Command to run tests 17 | script: 18 | - flake8 19 | - python -m pytest tests/ov 20 | # - python -m pytest tests/server_client # test also require openwsn-fw so for now not working on Travis 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to OpenVisualizer and OpenWSN 2 | :+1: :tada: First off, thanks for taking the time to contribute! :tada: :+1: 3 | 4 | We use JIRA to track issues and new features: [jira-issues](https://openwsn.atlassian.net/projects/OV/issues) 5 | 6 | We use `flake8` to enforce the Python PEP-8 style guide. The Travis builder verifies new pull requests and it fails if the Python code does not follow the style guide. 7 | 8 | You can check locally if your code changes comply with PEP-8. First, install the main `flake8` package and two `flake8` plugins: 9 | 10 | ```bash 11 | $ pip install flake8 12 | $ pip install pep8-naming 13 | $ pip install flake8-commas 14 | ``` 15 | 16 | Move to the root of the OpenVisualizer project and run: 17 | 18 | ```bash 19 | $ flake8 --config=tox.ini 20 | ``` 21 | 22 | If `flake8` does not generate any output, your code passes the test; alternatively, you can check the return code: 23 | 24 | ```bash 25 | $ flake8 --config=tox.ini 26 | $ echo $? 27 | 0 28 | ``` 29 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | label 'linux && debian' 4 | } 5 | stages { 6 | stage('Test') { 7 | steps { 8 | sh 'flake8 > code_format_report.log' 9 | sh 'python -m pytest tests/ov --junitxml=report.xml -s' 10 | } 11 | } 12 | } 13 | post { 14 | always { 15 | junit 'report.xml' 16 | archiveArtifacts artifacts: '*.log', onlyIfSuccessful: true 17 | cleanWs() 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c), Regents of the University of California. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | - Neither the name of the Regents of the University of California nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include LICENSE.md 3 | recursive-include openvisualizer/config *.conf 4 | recursive-include openvisualizer/topologies * 5 | recursive-include images *.png 6 | recursive-include openvisualizer/client/web_files/static/css * 7 | recursive-include openvisualizer/client/web_files/static/font-awesome/css * 8 | recursive-include openvisualizer/client/web_files/static/font-awesome/fonts * 9 | recursive-include openvisualizer/client/web_files/static/images * 10 | recursive-include openvisualizer/client/web_files/static/js *.js 11 | recursive-include openvisualizer/client/web_files/static/js/plugins/metisMenu * 12 | recursive-include openvisualizer/client/web_files/templates * 13 | -------------------------------------------------------------------------------- /docs/bspemulator.rst: -------------------------------------------------------------------------------- 1 | Package: bspemulator 2 | ==================== 3 | 4 | :mod:`bspboard` Module 5 | ---------------------- 6 | 7 | .. automodule:: openvisualizer.bspemulator.bspboard 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`bspdebugpins` Module 13 | -------------------------- 14 | 15 | .. automodule:: openvisualizer.bspemulator.bspdebugpins 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`bspeui64` Module 21 | ---------------------- 22 | 23 | .. automodule:: openvisualizer.bspemulator.bspeui64 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`bspleds` Module 29 | --------------------- 30 | 31 | .. automodule:: openvisualizer.bspemulator.bspleds 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`bspmodule` Module 37 | ----------------------- 38 | 39 | .. automodule:: openvisualizer.bspemulator.bspmodule 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | :mod:`bspradio` Module 45 | ---------------------- 46 | 47 | .. automodule:: openvisualizer.bspemulator.bspradio 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | 52 | :mod:`bspsctimer` Module 53 | --------------------------- 54 | 55 | .. automodule:: openvisualizer.bspemulator.bspsctimer 56 | :members: 57 | :undoc-members: 58 | :show-inheritance: 59 | 60 | :mod:`bspuart` Module 61 | --------------------- 62 | 63 | .. automodule:: openvisualizer.bspemulator.bspuart 64 | :members: 65 | :undoc-members: 66 | :show-inheritance: 67 | 68 | :mod:`hwcrystal` Module 69 | ----------------------- 70 | 71 | .. automodule:: openvisualizer.bspemulator.hwcrystal 72 | :members: 73 | :undoc-members: 74 | :show-inheritance: 75 | 76 | :mod:`hwmodule` Module 77 | ---------------------- 78 | 79 | .. automodule:: openvisualizer.bspemulator.hwmodule 80 | :members: 81 | :undoc-members: 82 | :show-inheritance: 83 | 84 | :mod:`hwsupply` Module 85 | ---------------------- 86 | 87 | .. automodule:: openvisualizer.bspemulator.hwsupply 88 | :members: 89 | :undoc-members: 90 | :show-inheritance: 91 | 92 | -------------------------------------------------------------------------------- /docs/eventbus.rst: -------------------------------------------------------------------------------- 1 | Package: eventbus 2 | ================== 3 | 4 | :mod:`eventbusclient` Module 5 | ---------------------------- 6 | 7 | .. automodule:: openvisualizer.eventbus.eventbusclient 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`eventbusmonitor` Module 13 | ----------------------------- 14 | 15 | .. automodule:: openvisualizer.eventbus.eventbusmonitor 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. OpenVisualizer documentation master file, created by 2 | sphinx-quickstart on Fri Jul 12 23:55:43 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to OpenVisualizer's documentation! 7 | ========================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 5 13 | 14 | bspemulator 15 | eventbus 16 | jrc 17 | moteconnector 18 | openparser 19 | moteprobe 20 | motestate 21 | openlbr 22 | opentun 23 | rpl 24 | simengine 25 | 26 | Indices and tables 27 | ================== 28 | 29 | * :ref:`genindex` 30 | * :ref:`modindex` 31 | * :ref:`search` 32 | 33 | -------------------------------------------------------------------------------- /docs/jrc.rst: -------------------------------------------------------------------------------- 1 | Package: jrc 2 | ================== 3 | 4 | :mod:`jrc` Module 5 | --------------------------- 6 | 7 | .. automodule:: openvisualizer.jrc.jrc 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`cojp_defines` Module 13 | --------------------------- 14 | 15 | .. automodule:: openvisualizer.jrc.cojp_defines 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/moteconnector.rst: -------------------------------------------------------------------------------- 1 | Package: moteconnector 2 | ======================= 3 | 4 | :mod:`moteconnector` Module 5 | --------------------------- 6 | 7 | .. automodule:: openvisualizer.motehandler.moteconnector.moteconnector 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | -------------------------------------------------------------------------------- /docs/moteprobe.rst: -------------------------------------------------------------------------------- 1 | Package: moteprobe 2 | ================== 3 | 4 | :mod:`openhdlc` Module 5 | ---------------------- 6 | 7 | .. automodule:: openvisualizer.motehandler.moteprobe.openhdlc 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`moteprobe` Module 13 | ----------------------- 14 | 15 | .. automodule:: openvisualizer.motehandler.moteprobe.moteprobe 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`serialtester` Module 21 | -------------------------- 22 | 23 | .. automodule:: openvisualizer.motehandler.moteprobe.serialtester 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/motestate.rst: -------------------------------------------------------------------------------- 1 | Package: motestate 2 | ================== 3 | 4 | :mod:`motestate` Module 5 | ----------------------- 6 | 7 | .. automodule:: openvisualizer.motehandler.motestate.motestate 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`elements` Module 13 | ----------------------- 14 | 15 | .. automodule:: openvisualizer.motehandler.motestate.elements 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`opentype` Module 21 | ----------------------- 22 | 23 | .. automodule:: openvisualizer.motehandler.motestate.opentype 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/openlbr.rst: -------------------------------------------------------------------------------- 1 | Package: openlbr 2 | ================= 3 | 4 | :mod:`openlbr` Module 5 | --------------------- 6 | 7 | .. automodule:: openvisualizer.openlbr.openlbr 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`sixlowpan_frag` Module 13 | ---------------------------- 14 | 15 | .. automodule:: openvisualizer.openlbr.sixlowpan_frag 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/openparser.rst: -------------------------------------------------------------------------------- 1 | Package: openparser 2 | ===================== 3 | 4 | :mod:`openparser` Module 5 | ------------------------ 6 | 7 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.openparser 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`parser` Module 13 | -------------------- 14 | 15 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.parser 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`parserdata` Module 21 | ------------------------ 22 | 23 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.parserdata 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`parserexception` Module 29 | ----------------------------- 30 | 31 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.parserexception 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`parseriec` Module 37 | ------------------------------------- 38 | 39 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.parseriec 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | :mod:`parserstatus` Module 45 | -------------------------- 46 | 47 | .. automodule:: openvisualizer.motehandler.moteconnector.openparser.parserstatus 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | -------------------------------------------------------------------------------- /docs/opentun.rst: -------------------------------------------------------------------------------- 1 | Package: opentun 2 | ================ 3 | 4 | :mod:`opentun` Module 5 | --------------------- 6 | 7 | .. automodule:: openvisualizer.opentun.opentun 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`opentunlinux` Module 13 | -------------------------- 14 | 15 | .. automodule:: openvisualizer.opentun.opentunlinux 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`opentunwindows` Module 21 | ---------------------------- 22 | 23 | .. automodule:: openvisualizer.opentun.opentunlinux 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`opentunmacos` Module 29 | ---------------------------- 30 | 31 | .. automodule:: openvisualizer.opentun.opentunmacos 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`opentunnull` Module 37 | ---------------------------- 38 | 39 | .. automodule:: openvisualizer.opentun.opentunnull 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | -------------------------------------------------------------------------------- /docs/rpl.rst: -------------------------------------------------------------------------------- 1 | Package: rpl 2 | ============ 3 | 4 | :mod:`rpl` Module 5 | ----------------- 6 | 7 | .. automodule:: openvisualizer.rpl.rpl 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`sourceroute` Module 13 | --------------------------- 14 | 15 | .. automodule:: openvisualizer.rpl.sourceroute 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`topology` Module 21 | ---------------------- 22 | 23 | .. automodule:: openvisualizer.rpl.topology 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | -------------------------------------------------------------------------------- /docs/simengine.rst: -------------------------------------------------------------------------------- 1 | Package: simengine 2 | ================== 3 | 4 | :mod:`idmanager` Module 5 | ----------------------- 6 | 7 | .. automodule:: openvisualizer.simengine.idmanager 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`locationmanager` Module 13 | ----------------------------- 14 | 15 | .. automodule:: openvisualizer.simengine.locationmanager 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`motehandler` Module 21 | ------------------------- 22 | 23 | .. automodule:: openvisualizer.simengine.motehandler 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`propagation` Module 29 | ------------------------- 30 | 31 | .. automodule:: openvisualizer.simengine.propagation 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`simengine` Module 37 | ----------------------- 38 | 39 | .. automodule:: openvisualizer.simengine.simengine 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | :mod:`timeLine` Module 45 | ---------------------- 46 | 47 | .. automodule:: openvisualizer.simengine.timeline 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | 52 | -------------------------------------------------------------------------------- /images/openv-client-motes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-client-motes.png -------------------------------------------------------------------------------- /images/openv-client-root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-client-root.png -------------------------------------------------------------------------------- /images/openv-client-views.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-client-views.png -------------------------------------------------------------------------------- /images/openv-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-client.png -------------------------------------------------------------------------------- /images/openv-serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-serial.png -------------------------------------------------------------------------------- /images/openv-tun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/openv-tun.png -------------------------------------------------------------------------------- /images/ov_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/ov_arch.png -------------------------------------------------------------------------------- /images/web_interface_topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/web_interface_topology.png -------------------------------------------------------------------------------- /images/webview-motes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/webview-motes.png -------------------------------------------------------------------------------- /images/webview-topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/images/webview-topology.png -------------------------------------------------------------------------------- /openvisualizer/__init__.py: -------------------------------------------------------------------------------- 1 | VERSION = '2.0.0' 2 | 3 | PACKAGE_NAME = 'openvisualizer' 4 | APPNAME = PACKAGE_NAME 5 | 6 | 7 | # cannot use os.path.join according to pkg_resources 8 | DEFAULT_LOGGING_CONF = '/'.join(("config", "logging.conf")) 9 | WINDOWS_COLORS = '/'.join(('config', 'colors_win.conf')) 10 | UNIX_COLORS = '/'.join(('config', 'colors_unix.conf')) 11 | -------------------------------------------------------------------------------- /openvisualizer/__main__.py: -------------------------------------------------------------------------------- 1 | from openvisualizer.main import main 2 | 3 | if __name__ == "__main__": 4 | main() 5 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/bspemulator/__init__.py -------------------------------------------------------------------------------- /openvisualizer/bspemulator/bspboard.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | from openvisualizer.bspemulator.bspmodule import BspModule 8 | 9 | 10 | class BspBoard(BspModule): 11 | """ Emulates the 'board' BSP module """ 12 | 13 | _name = 'BspBoard' 14 | 15 | def __init__(self, motehandler): 16 | # initialize the parent 17 | super(BspBoard, self).__init__(motehandler) 18 | 19 | # local variables 20 | self.timeline = self.engine.timeline 21 | 22 | # ======================== public ========================================== 23 | 24 | # === commands 25 | 26 | def cmd_init(self): 27 | """ Emulates: void board_init() """ 28 | 29 | # log the activity 30 | self.log.debug('cmd_init') 31 | 32 | # remember that module has been initialized 33 | self.is_initialized = True 34 | 35 | def cmd_sleep(self): 36 | """ Emulates: void board_init() """ 37 | 38 | try: 39 | # log the activity 40 | self.log.debug('cmd_sleep') 41 | 42 | self.motehandler.cpu_done.release() 43 | 44 | # block the mote until CPU is released by ISR 45 | self.motehandler.cpu_running.acquire() 46 | 47 | except Exception as err: 48 | self.log.critical(err) 49 | 50 | # ======================== private ========================================= 51 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/bspeui64.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | 9 | from openvisualizer.bspemulator.bspmodule import BspModule 10 | 11 | 12 | class BspEui64(BspModule): 13 | """ Emulates the 'eui64' BSP module """ 14 | 15 | _name = 'BspEui64' 16 | 17 | def __init__(self, motehandler): 18 | 19 | # initialize the parent 20 | super(BspEui64, self).__init__(motehandler) 21 | 22 | # ======================== public ========================================== 23 | 24 | # === commands 25 | 26 | def cmd_get(self): 27 | """ Emulates: void eui64_get(uint8_t* addressToWrite)""" 28 | 29 | # log the activity 30 | if self.log.isEnabledFor(logging.DEBUG): 31 | self.log.debug('cmd_get') 32 | 33 | # get my 16-bit ID 34 | my_id = self.motehandler.get_id() 35 | 36 | # format my EUI64 37 | my_eui64 = [0x14, 0x15, 0x92, 0xcc, 0x00, 0x00, ((my_id >> 8) & 0xff), ((my_id >> 0) & 0xff)] 38 | 39 | # log the activity 40 | if self.log.isEnabledFor(logging.DEBUG): 41 | self.log.debug('returning ' + str(my_eui64)) 42 | 43 | # respond 44 | return my_eui64 45 | 46 | # ======================== private ========================================= 47 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/bspmodule.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | import logging 7 | from abc import ABCMeta 8 | 9 | from openvisualizer.simengine import simengine 10 | 11 | 12 | class BspModule(object): 13 | """ Emulates the 'board' BSP module. """ 14 | __metaclass__ = ABCMeta 15 | 16 | @property 17 | def _name(self): 18 | raise NotImplementedError 19 | 20 | def __init__(self, motehandler): 21 | # store variables 22 | self.motehandler = motehandler 23 | 24 | # local variables 25 | self.is_initialized = False 26 | self.engine = simengine.SimEngine() 27 | 28 | # logging 29 | self.log = logging.getLogger(self._name + '_' + str(self.motehandler.get_id())) 30 | self.log.setLevel(logging.INFO) 31 | self.log.addHandler(logging.NullHandler()) 32 | 33 | # ======================== private ========================================= 34 | 35 | def _check_init(self): 36 | assert self.is_initialized 37 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/hwmodule.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | import logging 7 | from abc import ABCMeta 8 | 9 | from openvisualizer.simengine import simengine 10 | 11 | 12 | class HwModule(object): 13 | """ Parent class for all hardware modules. """ 14 | __metaclass__ = ABCMeta 15 | 16 | @property 17 | def _name(self): 18 | raise NotImplementedError 19 | 20 | def __init__(self, motehandler): 21 | # store variables 22 | self.motehandler = motehandler 23 | 24 | # local variables 25 | self.engine = simengine.SimEngine() 26 | 27 | # logging 28 | self.log = logging.getLogger(self._name + '_' + str(self.motehandler.get_id())) 29 | self.log.setLevel(logging.DEBUG) 30 | self.log.addHandler(logging.NullHandler()) 31 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/hwsupply.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2010-2013, Regents of the University of California. 3 | # All rights reserved. 4 | # 5 | # Released under the BSD 3-Clause license as published at the link below. 6 | # https://openwsn.atlassian.net/wiki/display/OW/License 7 | import logging 8 | 9 | from openvisualizer.bspemulator.hwmodule import HwModule 10 | 11 | 12 | class HwSupply(HwModule): 13 | """ Emulates the mote's power supply """ 14 | 15 | _name = 'HwModule' 16 | 17 | INTR_SWITCHON = 'hw_supply.switchOn' 18 | 19 | def __init__(self, motehandler): 20 | # initialize the parent 21 | super(HwSupply, self).__init__(motehandler) 22 | 23 | # local variables 24 | self.mote_on = False 25 | 26 | # ======================== public ========================================== 27 | 28 | def switch_on(self): 29 | 30 | # log the activity 31 | if self.log.isEnabledFor(logging.DEBUG): 32 | self.log.debug('switchOn') 33 | 34 | # filter error 35 | if self.mote_on: 36 | raise RuntimeError('mote already on') 37 | 38 | # change local variable 39 | self.mote_on = True 40 | 41 | # have the crystal start now 42 | self.motehandler.hw_crystal.start() 43 | 44 | # send command to mote 45 | self.motehandler.mote.supply_on() 46 | 47 | def switch_off(self): 48 | 49 | # log the activity 50 | if self.log.isEnabledFor(logging.DEBUG): 51 | self.log.debug('switchOff') 52 | 53 | # filter error 54 | if not self.mote_on: 55 | raise RuntimeError('mote already off') 56 | 57 | # change local variable 58 | self.mote_on = False 59 | 60 | def is_on(self): 61 | return self.mote_on 62 | 63 | # ======================== private ========================================= 64 | -------------------------------------------------------------------------------- /openvisualizer/bspemulator/vcdlogger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import threading 3 | 4 | 5 | class VcdLogger(object): 6 | ACTIVITY_DUR = 1000 # 1000ns=1us 7 | FILENAME = 'debugpins.vcd' 8 | FILENAME_SWAP = 'debugpins.vcd.swap' 9 | ENDVAR_LINE = '$upscope $end\n' 10 | ENDDEF_LINE = '$enddefinitions $end\n' 11 | 12 | # ======================== singleton pattern =============================== 13 | 14 | _instance = None 15 | _init = False 16 | 17 | SIGNAMES = ['frame', 'slot', 'fsm', 'task', 'isr', 'radio', 'ka', 'syncPacket', 'syncAck', 'debug'] 18 | 19 | def __new__(cls, *args, **kwargs): 20 | if not cls._instance: 21 | cls._instance = super(VcdLogger, cls).__new__(cls, *args, **kwargs) 22 | return cls._instance 23 | 24 | # ======================== main ============================================ 25 | 26 | def __init__(self): 27 | 28 | # don't re-initialize an instance (singleton pattern) 29 | if self._init: 30 | return 31 | self._init = True 32 | 33 | # local variables 34 | self.f = open(self.FILENAME, 'w') 35 | self.sig_name = {} 36 | self.last_ts = {} 37 | self.data_lock = threading.RLock() 38 | self.enabled = False 39 | self.sig_char = ord('!') 40 | 41 | # create header 42 | header = [] 43 | header += ['$timescale 1ns $end\n'] 44 | header += ['$scope module logic $end\n'] 45 | # variables will be declared here by self._addMote() 46 | header += [self.ENDVAR_LINE] 47 | header += [self.ENDDEF_LINE] 48 | header = ''.join(header) 49 | 50 | # write header 51 | self.f.write(header) 52 | 53 | # ======================== public ========================================== 54 | 55 | def set_enabled(self, enabled): 56 | assert enabled in [True, False] 57 | 58 | with self.data_lock: 59 | self.enabled = enabled 60 | 61 | def log(self, ts, mote, signal, state): 62 | 63 | assert signal in self.SIGNAMES 64 | assert state in [True, False] 65 | 66 | # stop here if not enables 67 | with self.data_lock: 68 | if not self.enabled: 69 | return 70 | 71 | # translate state to val 72 | if state: 73 | val = 1 74 | else: 75 | val = 0 76 | 77 | with self.data_lock: 78 | 79 | # add mote if needed 80 | if mote not in self.sig_name: 81 | self._add_mote(mote) 82 | 83 | # format 84 | output = [] 85 | ts_temp = int(ts * 1000000) * 1000 86 | if ((mote, signal) in self.last_ts) and self.last_ts[(mote, signal)] == ts: 87 | ts_temp += self.ACTIVITY_DUR 88 | output += ['#{0}\n'.format(ts_temp)] 89 | output += ['{0}{1}\n'.format(val, self.sig_name[mote][signal])] 90 | output = ''.join(output) 91 | 92 | # write 93 | self.f.write(output) 94 | 95 | # remember ts 96 | self.last_ts[(mote, signal)] = ts 97 | 98 | # ======================== private ========================================= 99 | 100 | def _add_mote(self, mote): 101 | assert mote not in self.sig_name 102 | 103 | # === populate sig_name 104 | self.sig_name[mote] = {} 105 | for signal in self.SIGNAMES: 106 | self.sig_name[mote][signal] = chr(self.sig_char) 107 | self.sig_char += 1 108 | 109 | # === close FILENAME 110 | self.f.close() 111 | 112 | # === FILENAME -> FILENAME_SWAP 113 | fswap = open(self.FILENAME_SWAP, 'w') 114 | for line in open(self.FILENAME, 'r'): 115 | # declare variables 116 | if line == self.ENDVAR_LINE: 117 | for signal in self.SIGNAMES: 118 | fswap.write( 119 | '$var wire 1 {0} {1}_{2} $end\n'.format( 120 | self.sig_name[mote][signal], 121 | mote, 122 | signal, 123 | ), 124 | ) 125 | # print line 126 | fswap.write(line) 127 | # initialize variables 128 | if line == self.ENDDEF_LINE: 129 | for signal in self.SIGNAMES: 130 | fswap.write('#0\n') 131 | fswap.write('0{0}\n'.format(self.sig_name[mote][signal])) 132 | fswap.close() 133 | 134 | # === FILENAME_SWAP -> FILENAME 135 | os.remove(self.FILENAME) 136 | os.rename(self.FILENAME_SWAP, self.FILENAME) 137 | 138 | # === re-open FILENAME 139 | self.f = open(self.FILENAME, 'a') 140 | -------------------------------------------------------------------------------- /openvisualizer/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/__init__.py -------------------------------------------------------------------------------- /openvisualizer/client/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # import all the view plugins 2 | 3 | from . import macstats # noqa: F401 4 | from . import motestatus # noqa: F401 5 | from . import msf # noqa: F401 6 | from . import neighbors # noqa: F401 7 | from . import pktqueue # noqa: F401 8 | from . import schedule # noqa: F401 9 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/macstats.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | 4 | from openvisualizer.client.plugins.plugin import Plugin 5 | from openvisualizer.client.view import View 6 | from openvisualizer.motehandler.motestate.motestate import MoteState 7 | 8 | 9 | @Plugin.record_view("macstats") 10 | class MacStats(View): 11 | def __init__(self, proxy, mote_id, refresh_rate): 12 | super(MacStats, self).__init__(proxy, mote_id, refresh_rate) 13 | 14 | self.title = 'macstats' 15 | 16 | def render(self, ms=None): 17 | super(MacStats, self).render() 18 | state_dict = json.loads(ms[MoteState.ST_MACSTATS])[0] 19 | for stat in state_dict: 20 | print('{:>13}:{:>20}'.format(str(stat), str(state_dict[stat]))) 21 | 22 | def run(self): 23 | logging.debug("Enabling blessed fullscreen") 24 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 25 | super(MacStats, self).run() 26 | logging.debug("Exiting blessed fullscreen") 27 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/motestatus.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import json 4 | import logging 5 | 6 | from openvisualizer.client.plugins.plugin import Plugin 7 | from openvisualizer.client.utils import transform_into_ipv6 8 | from openvisualizer.client.view import View 9 | from openvisualizer.motehandler.motestate.motestate import MoteState 10 | 11 | 12 | @Plugin.record_view("motestatus") 13 | class MoteStatus(View): 14 | def __init__(self, proxy, mote_id, refresh_rate): 15 | super(MoteStatus, self).__init__(proxy, mote_id, refresh_rate) 16 | 17 | self.title = 'motestatus' 18 | 19 | def render(self, ms=None): 20 | super(MoteStatus, self).render() 21 | 22 | is_sync = json.loads(ms[MoteState.ST_ISSYNC])[0] 23 | dagrank = json.loads(ms[MoteState.ST_MYDAGRANK])[0] 24 | id_manager = json.loads(ms[MoteState.ST_IDMANAGER])[0] 25 | joined = json.loads(ms[MoteState.ST_JOINED])[0] 26 | asn = json.loads(ms[MoteState.ST_ASN])[0] 27 | kaperiod = json.loads(ms[MoteState.ST_KAPERIOD])[0] 28 | 29 | print('\n', end='') 30 | _str = self._build_str(10, int(id_manager['isDAGroot'])) 31 | print(_str.format('DAG root', id_manager['isDAGroot'])) 32 | _str = self._build_str(6, int(is_sync['isSync'])) 33 | print(_str.format('Synchronized', is_sync['isSync'])) 34 | _str = self._build_str(3, int(joined['joinedAsn'], 16)) 35 | print(_str.format('Sec. Joined@ASN', int(joined['joinedAsn'], 16))) 36 | _str = self._build_str(10) 37 | print(_str.format('DAG rank', dagrank['myDAGrank'])) 38 | _str = self._build_str(12) 39 | print(_str.format('PAN ID', id_manager['myPANID'][:-8])) 40 | _str = self._build_str(6) 41 | print(_str.format('IPv6 address', 42 | transform_into_ipv6(id_manager['myPrefix'][:-9] + '-' + id_manager['my64bID'][:-5]))) 43 | 44 | print('\n', end='') 45 | _str = self._build_str(7) 46 | print(_str.format('Current ASN', asn['asn'])) 47 | 48 | print('\n', end='') 49 | _str = self._build_str(9) 50 | print(_str.format('KA Period', kaperiod['kaPeriod'])) 51 | 52 | def _build_str(self, offset, flag=None): 53 | b = self.term.bold 54 | n = self.term.normal 55 | red = self.term.bold_red 56 | green = self.term.bold_green 57 | 58 | if flag is None: 59 | return b + '{}' + n + self.term.move_right(offset) + '{}' 60 | 61 | if flag: 62 | return b + '{}' + n + self.term.move_right(offset) + green + '{}' + n 63 | else: 64 | return b + '{}' + n + self.term.move_right(offset) + red + '{}' + n 65 | 66 | def run(self): 67 | logging.debug("Enabling blessed fullscreen") 68 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 69 | super(MoteStatus, self).run() 70 | logging.debug("Exiting blessed fullscreen") 71 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/msf.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import logging 4 | 5 | from openvisualizer.client.plugins.plugin import Plugin 6 | from openvisualizer.client.view import View 7 | 8 | 9 | @Plugin.record_view("msf") 10 | class MSF(View): 11 | def __init__(self, proxy, mote_id, refresh_rate): 12 | super(MSF, self).__init__(proxy, mote_id, refresh_rate) 13 | 14 | self.title = 'msf' 15 | 16 | def render(self, ms=None): 17 | super(MSF, self).render() 18 | # msf_values = json.loads(ms[MoteState.ST_MSF]) 19 | print(self.term.bold_red + "Currently unavailable! Requires firmware update!" + self.term.normal) 20 | 21 | def run(self): 22 | logging.debug("Enabling blessed fullscreen") 23 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 24 | super(MSF, self).run() 25 | logging.debug("Exiting blessed fullscreen") 26 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/neighbors.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import json 4 | import logging 5 | from math import ceil 6 | 7 | from openvisualizer.client.plugins.plugin import Plugin 8 | from openvisualizer.client.view import View 9 | from openvisualizer.motehandler.motestate.motestate import MoteState 10 | 11 | 12 | @Plugin.record_view("neighbors") 13 | class Neighbors(View): 14 | COLOR_HDR_MARGIN = 7.5 15 | COLOR_LINE_MARGIN = 15 16 | 17 | def __init__(self, proxy, mote_id, refresh_rate): 18 | super(Neighbors, self).__init__(proxy, mote_id, refresh_rate) 19 | 20 | self.title = 'neighbors' 21 | 22 | def render(self, ms=None): 23 | super(Neighbors, self).render() 24 | neighbors = json.loads(ms[MoteState.ST_NEIGHBORS]) 25 | neighbor_rows = [] 26 | neighbor_info = {} 27 | 28 | yb = self.term.bold_yellow 29 | n = self.term.normal 30 | w = int(self.term.width / 2) 31 | 32 | for nb in neighbors: 33 | if int(nb['used']): 34 | neighbor_rows.append(nb) 35 | 36 | if len(neighbor_rows) == 0: 37 | print(self.term.red_bold + 'No neighbors for this mote' + self.term.normal) 38 | return 39 | 40 | addr_headers, stable_neighbor, join_prio = [], [], [] 41 | secure, rssi, backoff, dagrank = [], [], [], [] 42 | 43 | for neighbor in neighbor_rows: 44 | addr_headers.append('|{}{:^13s}{}'.format(yb, str(neighbor['addr'][-11:-5]).replace('-', ''), n)) 45 | stable_neighbor.append('|{:^13s}'.format('Y' if int((neighbor['stableNeighbor'])) else 'N')) 46 | secure.append('|{:^13s}'.format('Y' if not int((neighbor['insecure'])) else 'N')) 47 | rssi.append('|{:^13s}'.format(str((neighbor['rssi'])))) 48 | backoff.append('|{:^13s}'.format(str((neighbor['backoffExponent'])))) 49 | dagrank.append('|{:^13s}'.format(str((neighbor['DAGrank'])))) 50 | join_prio.append('|{:^13s}'.format(str((neighbor['joinPrio'])))) 51 | 52 | header = ''.join(addr_headers) + '|' 53 | line = ''.join(['-'] * (len(header) - len(addr_headers) * self.COLOR_LINE_MARGIN)) 54 | neighbor_info['Stable '] = ''.join(stable_neighbor) + '|' 55 | neighbor_info['Secure '] = ''.join(secure) + '|' 56 | neighbor_info['RSSI '] = ''.join(rssi) + '|' 57 | neighbor_info['BackOff Exponent '] = ''.join(backoff) + '|' 58 | neighbor_info['DAGrank '] = ''.join(dagrank) + '|' 59 | neighbor_info['Join Priority '] = ''.join(join_prio) + '|' 60 | 61 | print(line.rjust(abs(w + int(len(line) / 2)))) 62 | print(header.rjust(abs(w + int(len(header) / 2) + int(ceil(len(addr_headers) * self.COLOR_HDR_MARGIN) - 1)))) 63 | print(line.rjust(abs(w + int(len(line) / 2)))) 64 | 65 | for k, v in neighbor_info.items(): 66 | print(k.rjust(abs(w - int(len(v) / 2) - 1)), end='') 67 | print(v.rjust(abs(int(len(v) / 2)))) 68 | 69 | print(line.rjust(abs(w + int(len(line) / 2)))) 70 | 71 | def run(self): 72 | logging.debug("Enabling blessed fullscreen") 73 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 74 | super(Neighbors, self).run() 75 | logging.debug("Exiting blessed fullscreen") 76 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/pktqueue.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import json 4 | import logging 5 | import sys 6 | from collections import deque 7 | 8 | from openvisualizer.client.plugins.plugin import Plugin 9 | from openvisualizer.client.view import View 10 | from openvisualizer.motehandler.motestate.motestate import MoteState 11 | 12 | 13 | @Plugin.record_view("pktqueue") 14 | class PktQueue(View): 15 | def __init__(self, proxy, mote_id, refresh_rate, graphic=False): 16 | super(PktQueue, self).__init__(proxy, mote_id, refresh_rate) 17 | 18 | self.title = 'pktqueue' 19 | self.graphic = graphic 20 | 21 | # graphical view 22 | self.pkt_history = None 23 | self._build_pkt_history() 24 | 25 | def run(self): 26 | logging.debug("Enabling blessed fullscreen") 27 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 28 | super(PktQueue, self).run() 29 | logging.debug("Exiting blessed fullscreen") 30 | 31 | def _build_pkt_history(self): 32 | self.prv_width = self.term.width 33 | 34 | logging.info('Change in size terminal, recalculate figures') 35 | 36 | if self.pkt_history is None: 37 | self.pkt_history = deque([0] * (self.term.width - 10)) 38 | 39 | elif len(self.pkt_history) < self.term.width - 10: 40 | logging.debug("Appending to history: {}".format(self.term.width - 10 - len(self.pkt_history))) 41 | old = len(self.pkt_history) 42 | for i in range(self.term.width - 10 - len(self.pkt_history)): 43 | self.pkt_history.appendleft(0) 44 | logging.debug("Old {} vs new {}".format(old, len(self.pkt_history))) 45 | 46 | elif len(self.pkt_history) > self.term.width - 10: 47 | logging.debug("Popping from history: {}".format(len(self.pkt_history) - (self.term.width - 10))) 48 | old = len(self.pkt_history) 49 | for i in range(len(self.pkt_history) - (self.term.width - 10)): 50 | _ = self.pkt_history.popleft() 51 | logging.debug("Old {} vs new {}".format(old, len(self.pkt_history))) 52 | 53 | else: 54 | # they are equal, this should not happen 55 | pass 56 | 57 | # (re)build axis 58 | self.axis = ''.join(['-'] * (self.term.width - 10)) + '>' 59 | 60 | def render(self, ms=None): 61 | super(PktQueue, self).render() 62 | queue = json.loads(ms[MoteState.ST_QUEUE]) 63 | 64 | if not self.graphic: 65 | width = int(self.term.width / 2) 66 | print('{:>{}} {} {}'.format('OWNER', width - 5, '|', 'CREATOR')) 67 | 68 | bar = '----------------------------' 69 | print('{:>{}}'.format(bar, width + int(len(bar) / 2))) 70 | for row in queue: 71 | print('{:>{}} {} {}'.format(row['owner'], width - 5, '|', row['creator'])) 72 | 73 | else: 74 | if self.term.width != self.prv_width: 75 | self._build_pkt_history() 76 | 77 | pkt_count = 0 78 | for row in queue: 79 | if row['creator'] != '0 (NULL)': 80 | logging.debug('Occupied buffer space') 81 | pkt_count += 1 82 | 83 | self.pkt_history.popleft() 84 | self.pkt_history.append(pkt_count) 85 | logging.info("PktQueue history: {}".format(self.pkt_history)) 86 | 87 | grid = self._build_grid(len(queue)) 88 | 89 | print(self.term.bold + ' Every cursor block \'_\' represents: {}s'.format( 90 | self.refresh_rate) + self.term.normal) 91 | print('\n', end='') 92 | for row in grid: 93 | line = ''.join([' ' if x == 0 else self.term.on_green + ' ' + self.term.normal for x in row]) 94 | if max(row) > 0: 95 | print(self.term.move_right(4) + line.rjust(int(self.term.width / 2) + int(len(line) / 2))) 96 | 97 | print(self.axis.rjust(int(self.term.width / 2) + int(len(self.axis) / 2))) 98 | axis_text = '<-- time frame: {}s -->'.format(len(self.axis) * self.refresh_rate) 99 | print(axis_text.rjust(int(self.term.width / 2) + int(len(axis_text) / 2))) 100 | 101 | sys.stdout.flush() 102 | 103 | def _build_grid(self, queue_depth): 104 | grid = [[0 for x in range(len(self.pkt_history))] for y in range(queue_depth)] 105 | for x, pc in enumerate(self.pkt_history): 106 | if pc > 0: 107 | for i in range(pc): 108 | grid[len(grid) - 1 - i][x] = 1 109 | return grid 110 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/plugin.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from openvisualizer.client.view import View 4 | 5 | 6 | class Plugin(object): 7 | views = {} 8 | 9 | @classmethod 10 | def record_view(cls, view_id): 11 | """Decorator to record all the supported views dynamically""" 12 | 13 | def decorator(the_class): 14 | if not issubclass(the_class, View): 15 | raise ValueError("Can only decorate subclass of View") 16 | cls.views[view_id] = the_class 17 | return the_class 18 | 19 | return decorator 20 | -------------------------------------------------------------------------------- /openvisualizer/client/plugins/schedule.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | from math import ceil 4 | 5 | from openvisualizer.client.plugins.plugin import Plugin 6 | from openvisualizer.client.view import View 7 | from openvisualizer.motehandler.motestate.motestate import MoteState 8 | 9 | 10 | @Plugin.record_view("schedule") 11 | class Schedule(View): 12 | COLOR_LINE_MARGIN = 15 13 | COLOR_HDR_MARGIN = 7.5 14 | 15 | def __init__(self, proxy, mote_id, refresh_rate): 16 | super(Schedule, self).__init__(proxy, mote_id, refresh_rate) 17 | 18 | self.title = 'schedule' 19 | 20 | def render(self, ms=None): 21 | yb = self.term.bold_yellow 22 | n = self.term.normal 23 | 24 | columns = [] 25 | columns += ['|' + yb + ' Type ' + n] 26 | columns += ['|' + yb + ' S ' + n] 27 | # columns += ['|' + yb + ' A ' + n] 28 | columns += ['|' + yb + ' Nb ' + n] 29 | columns += ['|' + yb + ' SlotOf ' + n] 30 | columns += ['|' + yb + ' ChOf ' + n] 31 | columns += ['|' + yb + ' last ASN ' + n] 32 | columns += ['|' + yb + ' #TX ' + n] 33 | columns += ['|' + yb + ' #TX-ACK ' + n] 34 | columns += ['|' + yb + ' #RX ' + n + '|'] 35 | 36 | header = ''.join(columns) 37 | hdr_line = ''.join(['-'] * (len(header) - len(columns) * self.COLOR_LINE_MARGIN)) 38 | 39 | super(Schedule, self).render() 40 | schedule_rows = json.loads(ms[MoteState.ST_SCHEDULE]) 41 | 42 | active_cells = [] 43 | 44 | for row in schedule_rows: 45 | if row['type'] != '0 (OFF)': 46 | active_cells.append(row) 47 | 48 | active_cells.sort(key=lambda x: x['slotOffset']) 49 | 50 | w = int(self.term.width / 2) 51 | 52 | print(hdr_line.rjust(abs(w + int(len(hdr_line) / 2)))) 53 | print(header.rjust(abs(w + int(len(header) / 2) + int(ceil(len(columns) * self.COLOR_HDR_MARGIN))))) 54 | print(hdr_line.rjust(abs(w + int(len(hdr_line) / 2)))) 55 | 56 | for r in active_cells: 57 | c, shift = self._get_row_color(str(r['type'])[2:]) 58 | # r_str = '|{}{:^8s}{}|{:^3s}|{:^3s}|{:^6s}|{:^8s}|{:^6s}|{:^14s}|{:^5s}|{:^9s}|{:^5s}|'.format( 59 | r_str = '|{}{:^8s}{}|{:^3s}|{:^6s}|{:^8s}|{:^6s}|{:^14s}|{:^5s}|{:^9s}|{:^5s}|'.format( 60 | c, str(r['type'])[2:], n, 61 | 'X' if int(r['shared']) else ' ', 62 | # 'X' if int(r['isAutoCell']) else ' ', 63 | 'ANY' if 'anycast' in str(r['neighbor']) else str(r['neighbor'])[-11:-6].replace('-', ''), 64 | str(r['slotOffset']), 65 | str(r['channelOffset']), 66 | hex(int(str(r['lastUsedAsn']), 16)), 67 | str(r['numTx']), 68 | str(r['numTxACK']), 69 | str(r['numRx'])) 70 | 71 | print(r_str.rjust(abs(w + int(len(r_str) / 2) + shift))) 72 | 73 | print(hdr_line.rjust(abs(w + int(len(hdr_line) / 2)))) 74 | print('\n') 75 | print('{}{}:{}{:>15}'.format(yb, 'S', n, 'Shared cell?')) 76 | # print('{}{}:{}{:>19}'.format(yb, 'A', n, 'Autonomous cell?')) 77 | print('{}{}:{}{:>20}'.format(yb, 'Nb', n, '16-bit Neighbor ID')) 78 | 79 | def run(self): 80 | logging.debug("Enabling blessed fullscreen") 81 | with self.term.fullscreen(), self.term.cbreak(), self.term.hidden_cursor(): 82 | super(Schedule, self).run() 83 | logging.debug("Exiting blessed fullscreen") 84 | 85 | def _get_row_color(self, cell_type): 86 | if '(TXRX)' == cell_type: 87 | return self.term.purple, 12 88 | elif '(TX)' == cell_type: 89 | return self.term.blue, 6 90 | else: 91 | return self.term.red, 6 92 | -------------------------------------------------------------------------------- /openvisualizer/client/utils.py: -------------------------------------------------------------------------------- 1 | def transform_into_ipv6(ipv6_str): 2 | for i in range(16): 3 | if i % 2 == 0: 4 | ipv6_str = ipv6_str.replace('-', '', 1) 5 | else: 6 | ipv6_str = ipv6_str.replace('-', ':', 1) 7 | return ipv6_str.strip() 8 | -------------------------------------------------------------------------------- /openvisualizer/client/view.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import datetime 4 | import errno 5 | import logging 6 | import socket 7 | import threading 8 | import time 9 | from abc import ABCMeta, abstractmethod 10 | from xmlrpclib import Fault 11 | 12 | from blessed import Terminal 13 | 14 | 15 | class View(threading.Thread): 16 | __metaclass__ = ABCMeta 17 | 18 | def __init__(self, proxy, mote_id, refresh_rate): 19 | super(View, self).__init__() 20 | 21 | self.rpc_server = proxy.rpc_server 22 | self.term = Terminal() 23 | self.quit = False 24 | 25 | self.refresh_rate = refresh_rate 26 | self.mote_id = mote_id 27 | self.title = '' 28 | self.error_msg = '' 29 | 30 | def run(self): 31 | while not self.quit: 32 | try: 33 | mote_state = self.rpc_server.get_mote_state(self.mote_id) 34 | except Fault as err: 35 | logging.error("Caught fault from server") 36 | self.close() 37 | self.error_msg = err.faultString 38 | except socket.error as err: 39 | if errno.ECONNREFUSED: 40 | logging.error("Connection refused error") 41 | self.print_connrefused_msg() 42 | time.sleep(1) 43 | else: 44 | logging.critical("Unknown socket error") 45 | print(self.term.home + self.term.red_on_black + err) 46 | View.block() 47 | else: 48 | self.render(mote_state) 49 | time.sleep(self.refresh_rate) 50 | 51 | logging.info("Returning from thread") 52 | 53 | @abstractmethod 54 | def render(self, ms=None): 55 | print(self.term.home + self.term.clear()) 56 | print(self.term.home, end='') 57 | self.print_banner() 58 | 59 | def close(self): 60 | logging.info("Closing thread") 61 | self.quit = True 62 | 63 | @staticmethod 64 | def block(): 65 | while True: 66 | time.sleep(1) 67 | 68 | def print_banner(self): 69 | time = datetime.datetime.now().strftime("%H:%M:%S,%f") 70 | title, meta, ma, ra = self._build_banner() 71 | print(self.term.home + self.term.bold_white_on_seagreen + title + self.term.normal, end='') 72 | print(self.term.white_on_seagreen + meta.rjust(ma), end='') 73 | print(self.term.white_on_seagreen + '{:>{}}'.format(time, ra), end='') 74 | print(self.term.clear_eol() + self.term.normal + '\n') 75 | 76 | def print_connrefused_msg(self): 77 | msg = "CONN. REFUSED" 78 | title, meta, ma, ra = self._build_banner() 79 | print(self.term.home + self.term.bold_white_on_indianred + title + self.term.normal, end='') 80 | print(self.term.white_on_indianred + meta.rjust(ma), end='') 81 | print(self.term.white_on_indianred + '{:>{}}'.format(msg, ra), end='') 82 | print(self.term.clear_eol() + self.term.normal + '\n') 83 | 84 | def _build_banner(self): 85 | w = self.term.width 86 | title = '[{}]'.format(self.title) 87 | meta_info = 'MOTE-ID: {} -- RR: {}'.format(self.mote_id, self.refresh_rate) 88 | middle_aligned = abs(int(w / 2) + int(len(meta_info) / 2) - len(title)) 89 | right_aligned = abs(int(w / 2) - int(len(meta_info) / 2)) 90 | return title, meta_info, middle_aligned, right_aligned 91 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/css/d3.css: -------------------------------------------------------------------------------- 1 | svg { 2 | background-color: #c0c0c0; 3 | border: 0px solid black; 4 | overflow: hidden; 5 | } 6 | 7 | .node rect { 8 | stroke-width: 2px; 9 | stroke: #fff; 10 | fill: #fff; 11 | } 12 | 13 | #node-CLOSED rect { 14 | fill: #fff; 15 | } 16 | 17 | #node-ESTAB rect { 18 | fill: #fff; 19 | } 20 | 21 | .edgeLabel rect { 22 | fill: #fff; 23 | } 24 | 25 | .edgePath path { 26 | stroke: #fff; 27 | stroke-width: 2px; 28 | fill: none; 29 | } -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/css/openvisualizer.css: -------------------------------------------------------------------------------- 1 | /* Global Styles */ 2 | 3 | body { 4 | padding-top: 121px; 5 | background-color: #f8f8f8; 6 | } 7 | 8 | @media(min-width:768px) { 9 | body { 10 | padding-top: 11px; 11 | } 12 | } 13 | 14 | /* Wrappers */ 15 | 16 | #wrapper { 17 | width: 100%; 18 | } 19 | 20 | #page-wrapper { 21 | padding: 0 15px; 22 | min-height: 568px; 23 | background-color: #fff; 24 | } 25 | 26 | @media(min-width:768px) { 27 | #page-wrapper { 28 | position: inherit; 29 | margin: 0 0 0 185px; 30 | padding: 0 30px; 31 | min-height: 1300px; 32 | } 33 | } 34 | 35 | /* Navigation */ 36 | 37 | .navbar-top-links li { 38 | display: inline-block; 39 | } 40 | 41 | .navbar-top-links li:last-child { 42 | margin-right: 15px; 43 | } 44 | 45 | .navbar-top-links li a { 46 | padding: 15px; 47 | min-height: 50px; 48 | } 49 | 50 | .navbar-top-links .dropdown-menu li { 51 | display: block; 52 | } 53 | 54 | .navbar-top-links .dropdown-menu li:last-child { 55 | margin-right: 0; 56 | } 57 | 58 | .navbar-top-links .dropdown-menu li a { 59 | padding: 3px 20px; 60 | min-height: 0; 61 | } 62 | 63 | .navbar-top-links .dropdown-menu li a div { 64 | white-space: normal; 65 | } 66 | 67 | .navbar-top-links .dropdown-messages, 68 | .navbar-top-links .dropdown-tasks, 69 | .navbar-top-links .dropdown-alerts { 70 | width: 310px; 71 | min-width: 0; 72 | } 73 | 74 | .navbar-top-links .dropdown-messages { 75 | margin-left: 5px; 76 | } 77 | 78 | .navbar-top-links .dropdown-tasks { 79 | margin-left: -59px; 80 | } 81 | 82 | .navbar-top-links .dropdown-alerts { 83 | margin-left: -123px; 84 | } 85 | 86 | .navbar-top-links .dropdown-user { 87 | right: 0; 88 | left: auto; 89 | } 90 | 91 | /* Sidebar Menu Styles */ 92 | 93 | .navbar-static-side ul li { 94 | border-bottom: 1px solid #e7e7e7; 95 | } 96 | 97 | .sidebar-search { 98 | padding: 15px; 99 | } 100 | 101 | .arrow { 102 | float: right; 103 | } 104 | 105 | .fa.arrow:before { 106 | content: "\f104"; 107 | } 108 | 109 | .active > a > .fa.arrow:before { 110 | content: "\f107"; 111 | } 112 | 113 | .nav-second-level li, 114 | .nav-third-level li { 115 | border-bottom: none !important; 116 | } 117 | 118 | .nav-second-level li a { 119 | padding-left: 37px; 120 | } 121 | 122 | .nav-third-level li a { 123 | padding-left: 52px; 124 | } 125 | 126 | @media(min-width:768px) { 127 | .navbar-static-side { 128 | z-index: 1; 129 | position: absolute; 130 | width: 185px; 131 | } 132 | 133 | .navbar-top-links .dropdown-messages, 134 | .navbar-top-links .dropdown-tasks, 135 | .navbar-top-links .dropdown-alerts { 136 | margin-left: auto; 137 | } 138 | } 139 | 140 | @media(max-height:600px) and (max-width:767px) { 141 | .sidebar-collapse { 142 | max-height: 300px; 143 | overflow-y: scroll; 144 | } 145 | } 146 | 147 | @media(max-height:400px) and (max-width:767px) { 148 | .sidebar-collapse { 149 | max-height: 200px; 150 | overflow-y: scroll; 151 | } 152 | } 153 | 154 | /* Buttons */ 155 | 156 | .btn-outline { 157 | color: inherit; 158 | background-color: transparent; 159 | transition: all .5s; 160 | } 161 | 162 | .btn-primary.btn-outline { 163 | color: #428bca; 164 | } 165 | 166 | .btn-success.btn-outline { 167 | color: #5cb85c; 168 | } 169 | 170 | .btn-info.btn-outline { 171 | color: #5bc0de; 172 | } 173 | 174 | .btn-warning.btn-outline { 175 | color: #f0ad4e; 176 | } 177 | 178 | .btn-danger.btn-outline { 179 | color: #d9534f; 180 | } 181 | 182 | .btn-primary.btn-outline:hover, 183 | .btn-success.btn-outline:hover, 184 | .btn-info.btn-outline:hover, 185 | .btn-warning.btn-outline:hover, 186 | .btn-danger.btn-outline:hover { 187 | color: #fff; 188 | } 189 | 190 | /* Logo */ 191 | 192 | #logo { 193 | border-bottom: 3px solid #e7e7e7; 194 | } 195 | 196 | .logo-img { 197 | padding: 12px 42px; 198 | } 199 | 200 | /* Override */ 201 | 202 | a { 203 | color: #3070a7; 204 | } 205 | 206 | a :hover { 207 | color: #033f79; 208 | } 209 | 210 | .active { 211 | background-color: white; 212 | } 213 | 214 | .navbar { 215 | min-height: 11px !important; 216 | } 217 | 218 | .ovVersion { 219 | color: #c0c0c0; 220 | position: absolute; 221 | left: 50%; 222 | transform: translate(-50%, 0%); 223 | } -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/images/favicon.ico -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/images/openwsn_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/client/web_files/static/images/openwsn_logo.png -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/js/jiraissuecollector.js: -------------------------------------------------------------------------------- 1 | jQuery.ajax({ 2 | url: "https://openwsn.atlassian.net/s/54798a4e60287616bf09b42ff429b8fe-T/en_US-ga1ki/6328/26/1.4.13/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector-embededjs/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector-embededjs.js?locale=en-US&collectorId=d9d27949", 3 | type: "get", 4 | cache: true, 5 | dataType: "script" 6 | }); -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/js/openvisualizer.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | $('#side-menu').metisMenu(); 4 | 5 | }); 6 | 7 | //Loads the correct sidebar on window load, 8 | //collapses the sidebar on window resize. 9 | $(function() { 10 | $(window).bind("load resize", function() { 11 | width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; 12 | if (width < 768) { 13 | $('div.sidebar-collapse').addClass('collapse') 14 | } else { 15 | $('div.sidebar-collapse').removeClass('collapse') 16 | } 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/static/js/plugins/metisMenu/jquery.metisMenu.js: -------------------------------------------------------------------------------- 1 | ;(function ($, window, document, undefined) { 2 | 3 | var pluginName = "metisMenu", 4 | defaults = { 5 | toggle: true 6 | }; 7 | 8 | function Plugin(element, options) { 9 | this.element = element; 10 | this.settings = $.extend({}, defaults, options); 11 | this._defaults = defaults; 12 | this._name = pluginName; 13 | this.init(); 14 | } 15 | 16 | Plugin.prototype = { 17 | init: function () { 18 | 19 | var $this = $(this.element), 20 | $toggle = this.settings.toggle; 21 | 22 | $this.find('li.active').has('ul').children('ul').addClass('collapse in'); 23 | $this.find('li').not('.active').has('ul').children('ul').addClass('collapse'); 24 | 25 | $this.find('li').has('ul').children('a').on('click', function (e) { 26 | e.preventDefault(); 27 | 28 | $(this).parent('li').toggleClass('active').children('ul').collapse('toggle'); 29 | 30 | if ($toggle) { 31 | $(this).parent('li').siblings().removeClass('active').children('ul.in').collapse('hide'); 32 | } 33 | }); 34 | } 35 | }; 36 | 37 | $.fn[ pluginName ] = function (options) { 38 | return this.each(function () { 39 | if (!$.data(this, "plugin_" + pluginName)) { 40 | $.data(this, "plugin_" + pluginName, new Plugin(this, options)); 41 | } 42 | }); 43 | }; 44 | 45 | })(jQuery, window, document); 46 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/templates/connectivity.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %include head.tmpl 5 | 6 | 7 | 8 | 9 | 10 |
11 | %include navbar.tmpl ovVersion=ovVersion 12 | 13 |
14 |
15 |
16 |

Connectivity

17 |
18 |
19 | 20 | 83 | 84 |
85 |
86 |
Network Connectivity
87 |
88 | 89 | 90 | 91 |
92 |
93 |
94 |
95 | 96 | 97 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/templates/eventBus.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %include head.tmpl 5 | 6 | 7 |
8 | %include navbar.tmpl ovVersion=ovVersion 9 | 10 |
11 |
12 |
13 |

Event Bus

14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 | 31 | 55 |
56 | 57 |
58 |
59 |
60 | 102 |
103 | 104 | 132 |
133 |
134 |
135 | 136 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/templates/head.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenWSN 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/templates/navbar.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /openvisualizer/client/web_files/templates/routing.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %include head.tmpl 5 | 6 | 7 | 8 | 9 | 10 |
11 | %include navbar.tmpl ovVersion=ovVersion 12 | 13 |
14 |
15 |
16 |

Routing

17 |
18 |
19 | 20 | 83 | 84 |
85 |
86 |
Current RPL Routing
87 |
88 | 89 | 90 | 91 |
92 |
93 |
94 |
95 | 96 | 97 | -------------------------------------------------------------------------------- /openvisualizer/config/colors_unix.conf: -------------------------------------------------------------------------------- 1 | [levels] 2 | keys=debug, verbose, info, warning, success, error, critical 3 | 4 | [fields] 5 | keys=asctime, levelname 6 | 7 | # levels 8 | 9 | [debug] 10 | 11 | [verbose] 12 | color=81 13 | 14 | [info] 15 | 16 | [warning] 17 | color=166 18 | 19 | [success] 20 | color=83 21 | bold=True 22 | 23 | [error] 24 | color=124 25 | 26 | [critical] 27 | color=124 28 | bold=True 29 | 30 | # fields 31 | 32 | [asctime] 33 | color=35 34 | 35 | [levelname] 36 | color=31 37 | bold=True 38 | -------------------------------------------------------------------------------- /openvisualizer/config/colors_win.conf: -------------------------------------------------------------------------------- 1 | [levels] 2 | keys = debug, verbose, info, warning, success, error, critical 3 | 4 | [fields] 5 | keys = asctime, levelname 6 | 7 | # levels 8 | 9 | [debug] 10 | 11 | [verbose] 12 | color=magenta 13 | bold=True 14 | 15 | [info] 16 | 17 | [warning] 18 | color=yellow 19 | 20 | [success] 21 | color=green 22 | bold=True 23 | 24 | [error] 25 | color=red 26 | bold=True 27 | 28 | [critical] 29 | color=red 30 | bold=True 31 | 32 | # fields 33 | 34 | [asctime] 35 | color=cyan 36 | 37 | [levelname] 38 | color=cyan 39 | bold=True 40 | 41 | -------------------------------------------------------------------------------- /openvisualizer/config/logging.conf: -------------------------------------------------------------------------------- 1 | #============================ formatters ====================================== 2 | 3 | [formatters] 4 | keys=std, console 5 | 6 | [formatter_std] 7 | format=%(asctime)s %(levelname)s %(message)s 8 | datefmt=%H:%M:%S 9 | 10 | [formatter_console] 11 | class=openvisualizer.main.ColoredFormatter 12 | format=%(asctime)s %(levelname)s %(message)s 13 | datefmt=%H:%M:%S 14 | 15 | #============================ handlers ======================================== 16 | 17 | [handlers] 18 | keys=std, console 19 | 20 | [handler_std] 21 | class=handlers.RotatingFileHandler 22 | # args: filename, open mode, max file size, backup file count 23 | args=('openv-server.log',) 24 | formatter=std 25 | 26 | [handler_console] 27 | class=StreamHandler 28 | args=() 29 | formatter=console 30 | 31 | #============================ loggers ========================================= 32 | 33 | [loggers] 34 | keys= 35 | root, 36 | EventBusMonitor, 37 | EventBusClient, 38 | OpenTun, 39 | OpenTunMacOS, 40 | OpenTunWindows, 41 | OpenTunLinux, 42 | MoteConnector, 43 | MoteProbe, 44 | MoteState, 45 | OpenParser, 46 | ParserData, 47 | ParserPrintf, 48 | ParserLogs, 49 | ParserStatus, 50 | Parser, 51 | OpenHdlc, 52 | OpenLbr, 53 | SixLowPanFrag, 54 | RPL, 55 | SourceRoute, 56 | JRC, 57 | Topology, 58 | OpenVisualizerServer, 59 | Utils, 60 | OVtracer, 61 | CoAP 62 | 63 | [logger_root] 64 | level=ERROR 65 | handlers=std 66 | 67 | [logger_EventBusMonitor] 68 | level=ERROR 69 | handlers=std 70 | propagate=0 71 | qualname=EventBusMonitor 72 | 73 | [logger_EventBusClient] 74 | level=ERROR 75 | handlers=std 76 | propagate=0 77 | qualname=EventBusClient 78 | 79 | [logger_OpenTun] 80 | level=ERROR 81 | handlers=std 82 | propagate=0 83 | qualname=OpenTun 84 | 85 | [logger_OpenTunWindows] 86 | level=INFO 87 | handlers=std, console 88 | propagate=0 89 | qualname=OpenTunWindows 90 | 91 | [logger_OpenTunLinux] 92 | level=INFO 93 | handlers=std, console 94 | propagate=0 95 | qualname=OpenTunLinux 96 | 97 | [logger_OpenTunMacOS] 98 | level=INFO 99 | handlers=std, console 100 | propagate=0 101 | qualname=OpenTunMacOS 102 | 103 | [logger_MoteConnector] 104 | level=ERROR 105 | handlers=std 106 | propagate=0 107 | qualname=MoteConnector 108 | 109 | [logger_MoteProbe] 110 | level=INFO 111 | handlers=std, console 112 | propagate=0 113 | qualname=MoteProbe 114 | 115 | [logger_MoteState] 116 | level=ERROR 117 | handlers=std 118 | propagate=0 119 | qualname=MoteState 120 | 121 | [logger_OpenParser] 122 | level=ERROR 123 | handlers=std 124 | propagate=0 125 | qualname=OpenParser 126 | 127 | [logger_Parser] 128 | level=ERROR 129 | handlers=std 130 | propagate=0 131 | qualname=Parser 132 | 133 | [logger_ParserLogs] 134 | level=VERBOSE 135 | handlers=std, console 136 | propagate=0 137 | qualname=ParserLogs 138 | 139 | [logger_ParserData] 140 | level=INFO 141 | handlers=std, console 142 | propagate=0 143 | qualname=ParserData 144 | 145 | [logger_ParserPrintf] 146 | level=INFO 147 | handlers=std, console 148 | propagate=0 149 | qualname=ParserPrintf 150 | 151 | [logger_ParserPacket] 152 | level=ERROR 153 | handlers=std 154 | propagate=0 155 | qualname=ParserPacket 156 | 157 | [logger_ParserStatus] 158 | level=ERROR 159 | handlers=std 160 | propagate=0 161 | qualname=ParserStatus 162 | 163 | [logger_OpenHdlc] 164 | level=ERROR 165 | handlers=std 166 | propagate=0 167 | qualname=OpenHdlc 168 | 169 | [logger_OpenLbr] 170 | level=ERROR 171 | handlers=std 172 | propagate=0 173 | qualname=OpenLbr 174 | 175 | [logger_SixLowPanFrag] 176 | level=INFO 177 | handlers=std, console 178 | propagate=0 179 | qualname=SixLowPanFrag 180 | 181 | [logger_RPL] 182 | level=INFO 183 | handlers=std, console 184 | propagate=0 185 | qualname=RPL 186 | 187 | [logger_SourceRoute] 188 | level=ERROR 189 | handlers=std 190 | propagate=0 191 | qualname=SourceRoute 192 | 193 | [logger_JRC] 194 | level=VERBOSE 195 | handlers=std, console 196 | propagate=0 197 | qualname=JRC 198 | 199 | [logger_Topology] 200 | level=ERROR 201 | handlers=std 202 | propagate=0 203 | qualname=Topology 204 | 205 | [logger_OpenVisualizerServer] 206 | level=VERBOSE 207 | handlers=std, console 208 | propagate=0 209 | qualname=OpenVisualizerServer 210 | 211 | [logger_Utils] 212 | level=VERBOSE 213 | handlers=std, console 214 | propagate=0 215 | qualname=Utils 216 | 217 | [logger_OVtracer] 218 | level=INFO 219 | handlers=std, console 220 | propagate=0 221 | qualname=OVtracer 222 | 223 | [logger_CoAP] 224 | level=INFO 225 | handlers=std, console 226 | propagate=0 227 | qualname=coap 228 | 229 | -------------------------------------------------------------------------------- /openvisualizer/config/trace.conf: -------------------------------------------------------------------------------- 1 | # Note Expects 'logDir' passed in with location for file output. 2 | 3 | #============================ formatters ====================================== 4 | 5 | [formatters] 6 | keys=std,console 7 | 8 | [formatter_std] 9 | format=%(asctime)s [%(name)s:%(levelname)s] %(message)s 10 | datefmt= 11 | 12 | [formatter_console] 13 | format=%(asctime)s %(levelname)s %(message)s 14 | datefmt=%H:%M:%S 15 | 16 | #============================ handlers ======================================== 17 | 18 | [handlers] 19 | keys=std,console 20 | 21 | [handler_std] 22 | class=handlers.RotatingFileHandler 23 | # args: filename, open mode, max file size, backup file count 24 | args=('%(logDir)s/tracer.log', 'a', 20000000, 5) 25 | formatter=std 26 | 27 | [handler_console] 28 | class=StreamHandler 29 | args=() 30 | formatter=console 31 | 32 | #============================ loggers ========================================= 33 | 34 | [loggers] 35 | keys=root,OVtracer 36 | 37 | [logger_root] 38 | level=ERROR 39 | handlers=std 40 | 41 | [logger_OVtracer] 42 | level=INFO 43 | handlers=std, console 44 | propagate=0 45 | qualname=OVtracer 46 | 47 | -------------------------------------------------------------------------------- /openvisualizer/eventbus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/eventbus/__init__.py -------------------------------------------------------------------------------- /openvisualizer/eventbus/eventbusclient.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import threading 9 | 10 | from pydispatch import dispatcher 11 | 12 | log = logging.getLogger('EventBusClient') 13 | log.setLevel(logging.ERROR) 14 | log.addHandler(logging.NullHandler()) 15 | 16 | 17 | class EventBusClient(object): 18 | WILDCARD = '*' 19 | 20 | PROTO_ICMPv6 = 'icmpv6' 21 | PROTO_UDP = 'udp' 22 | PROTO_ALL = [ 23 | PROTO_ICMPv6, 24 | PROTO_UDP, 25 | ] 26 | 27 | def __init__(self, name, registrations): 28 | 29 | assert type(name) == str 30 | assert type(registrations) == list 31 | for r in registrations: 32 | assert type(r) == dict 33 | for k in r.keys(): 34 | assert k in ['signal', 'sender', 'callback'] 35 | 36 | # log 37 | log.debug("create instance") 38 | 39 | # store params 40 | self.data_lock = threading.RLock() 41 | self.registrations = [] 42 | 43 | # give this thread a name 44 | self.name = name 45 | 46 | # local variables 47 | self.go_on = True 48 | 49 | # register registrations 50 | for r in registrations: 51 | self.register(sender=r['sender'], signal=r['signal'], callback=r['callback']) 52 | 53 | # connect to dispatcher 54 | dispatcher.connect(receiver=self._event_bus_notification) 55 | 56 | # ======================== public ========================================== 57 | 58 | def dispatch(self, signal, data): 59 | return dispatcher.send(sender=self.name, signal=signal, data=data) 60 | 61 | def register(self, sender, signal, callback): 62 | 63 | # detect duplicate registrations 64 | with self.data_lock: 65 | for reg in self.registrations: 66 | if reg['sender'] == sender and reg['signal'] == signal and reg['callback'] == callback: 67 | raise SystemError( 68 | "Duplicate registration of sender={0} signal={1} callback={2}".format( 69 | sender, 70 | signal, 71 | callback, 72 | ), 73 | ) 74 | 75 | # register 76 | new_registration = { 77 | 'sender': sender, 78 | 'signal': signal, 79 | 'callback': callback, 80 | 'numRx': 0, 81 | } 82 | 83 | with self.data_lock: 84 | self.registrations += [new_registration] 85 | 86 | def unregister(self, sender, signal, callback): 87 | 88 | with self.data_lock: 89 | for reg in self.registrations: 90 | if reg['sender'] == sender and self._signals_equivalent(reg['signal'], signal) and \ 91 | reg['callback'] == callback: 92 | self.registrations.remove(reg) 93 | 94 | # ======================== private ========================================= 95 | 96 | def _event_bus_notification(self, signal, sender, data): 97 | 98 | callback = None 99 | 100 | # find the callback 101 | with self.data_lock: 102 | for r in self.registrations: 103 | if self._signals_equivalent(r['signal'], signal) and ( 104 | r['sender'] == sender or r['sender'] == self.WILDCARD): 105 | callback = r['callback'] 106 | break 107 | 108 | if not callback: 109 | return None 110 | 111 | # call the callback 112 | try: 113 | return callback(sender=sender, signal=signal, data=data) 114 | except TypeError as err: 115 | output = "ERROR could not call {0}, err={1}".format(callback, err) 116 | log.critical(output) 117 | print output 118 | 119 | def _signals_equivalent(self, s1, s2): 120 | return_val = True 121 | if type(s1) == type(s2) == str: 122 | if (s1 != s2) and (s1 != self.WILDCARD) and (s2 != self.WILDCARD): 123 | return_val = False 124 | elif type(s1) == type(s2) == tuple: 125 | if len(s1) == len(s2) == 3: 126 | for i in range(3): 127 | if (s1[i] != s2[i]) and (s1[i] != self.WILDCARD) and (s2[i] != self.WILDCARD): 128 | return_val = False 129 | else: 130 | return_val = False 131 | else: 132 | return_val = False 133 | 134 | return return_val 135 | 136 | def _dispatch_protocol(self, signal, data): 137 | """ used to sent to the eventbus a signal and look whether someone responds or not""" 138 | temp = self.dispatch(signal=signal, data=data) 139 | for (function, return_val) in temp: 140 | if return_val is not None: 141 | if log.isEnabledFor(logging.DEBUG): 142 | log.debug("returning true is subscribed to signal {0}, {1}".format(signal, return_val)) 143 | return True 144 | 145 | if log.isEnabledFor(logging.DEBUG): 146 | log.debug("returning false as nobody is subscribed to signal {0}, {1}".format(signal, temp)) 147 | 148 | return False 149 | 150 | def _dispatch_and_get_result(self, signal, data): 151 | temp = self.dispatch(signal=signal, data=data) 152 | for (function, return_val) in temp: 153 | if return_val is not None: 154 | return return_val 155 | raise SystemError('No answer to signal _dispatch_and_get_result') 156 | -------------------------------------------------------------------------------- /openvisualizer/jrc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/jrc/__init__.py -------------------------------------------------------------------------------- /openvisualizer/jrc/cojp_defines.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Copyright (c) 2010-2020, Regents of the University of California. 3 | # All rights reserved. 4 | # 5 | # Released under the BSD 3-Clause license as published at the link below. 6 | # https://openwsn.atlassian.net/wiki/display/OW/License 7 | """ 8 | 9 | from enum import IntEnum 10 | 11 | 12 | class CoJPLabel(IntEnum): 13 | COJP_PARAMETERS_LABELS_ROLE = 1 # Identifies the role parameter 14 | COJP_PARAMETERS_LABELS_LLKEYSET = 2 # Identifies the array carrying one or more link-layer cryptographic keys 15 | COJP_PARAMETERS_LABELS_LLSHORTADDRESS = 3 # Identifies the assigned link-layer short address 16 | COJP_PARAMETERS_LABELS_JRCADDRESS = 4 # Identifies the IPv6 address of the jrc 17 | COJP_PARAMETERS_LABELS_NETID = 5 # Identifies the network identifier (PAN ID) 18 | COJP_PARAMETERS_LABELS_NETPREFIX = 6 # Identifies the IPv6 prefix of the network 19 | 20 | 21 | class CoJPRole(IntEnum): 22 | COJP_ROLE_6N = 0 # 6TiSCH Node 23 | COJP_ROLE_6LBR = 1 # 6LBR Node 24 | 25 | 26 | class CoJPKeyUsage(IntEnum): 27 | COJP_KEY_USAGE_6TiSCH_K1K2_ENC_MIC32 = 0 28 | COJP_KEY_USAGE_6TiSCH_K1K2_ENC_MIC64 = 1 29 | COJP_KEY_USAGE_6TiSCH_K1K2_ENC_MIC128 = 2 30 | COJP_KEY_USAGE_6TiSCH_K1K2_MIC32 = 3 31 | COJP_KEY_USAGE_6TiSCH_K1K2_MIC64 = 4 32 | COJP_KEY_USAGE_6TiSCH_K1K2_MIC128 = 5 33 | COJP_KEY_USAGE_6TiSCH_K1_MIC32 = 6 34 | COJP_KEY_USAGE_6TiSCH_K1_MIC64 = 7 35 | COJP_KEY_USAGE_6TiSCH_K1_MIC128 = 8 36 | COJP_KEY_USAGE_6TiSCH_K2_MIC32 = 9 37 | COJP_KEY_USAGE_6TiSCH_K2_MIC64 = 10 38 | COJP_KEY_USAGE_6TiSCH_K2_MIC128 = 11 39 | COJP_KEY_USAGE_6TiSCH_K2_ENC_MIC32 = 12 40 | COJP_KEY_USAGE_6TiSCH_K2_ENC_MIC64 = 13 41 | COJP_KEY_USAGE_6TiSCH_K2_ENC_MIC128 = 14 42 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/motehandler/__init__.py -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/motehandler/moteconnector/__init__.py -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/moteconnector.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import socket 9 | import threading 10 | 11 | from pydispatch import dispatcher 12 | 13 | from openvisualizer.eventbus.eventbusclient import EventBusClient 14 | from openvisualizer.motehandler.moteconnector.openparser import openparser, parserexception 15 | from openvisualizer.motehandler.motestate.motestate import MoteState 16 | 17 | log = logging.getLogger('MoteConnector') 18 | log.setLevel(logging.ERROR) 19 | log.addHandler(logging.NullHandler()) 20 | 21 | 22 | class MoteConnector(EventBusClient): 23 | 24 | def __init__(self, mote_probe, stack_defines, mqtt_broker): 25 | 26 | # log 27 | log.debug("create instance") 28 | 29 | self.mote_probe = mote_probe 30 | self.stack_defines = stack_defines 31 | # store params 32 | self.serialport = self.mote_probe.portname 33 | 34 | # local variables 35 | self.parser = openparser.OpenParser(mqtt_broker, stack_defines, self.serialport) 36 | self.state_lock = threading.Lock() 37 | self.network_prefix = None 38 | self._subscribed_data_for_dagroot = False 39 | 40 | # give this thread a name 41 | self.name = 'mote_connector@{0}'.format(self.serialport) 42 | 43 | super(MoteConnector, self).__init__( 44 | name=self.name, 45 | registrations=[ 46 | { 47 | 'sender': self.WILDCARD, 48 | 'signal': 'infoDagRoot', 49 | 'callback': self._info_dag_root_handler, 50 | }, 51 | { 52 | 'sender': self.WILDCARD, 53 | 'signal': 'cmdToMote', 54 | 'callback': self._cmd_to_mote_handler, 55 | }, 56 | ], 57 | ) 58 | 59 | self.mote_probe.send_to_parser = self._send_to_parser 60 | self.received_status_notif = None 61 | 62 | def _send_to_parser(self, data): 63 | 64 | # log 65 | log.debug("received input={0}".format(data)) 66 | 67 | # parse input 68 | try: 69 | (event_sub_type, parsed_notif) = self.parser.parse_input(data) 70 | assert isinstance(event_sub_type, str) 71 | except parserexception.ParserException as err: 72 | # log 73 | log.error(str(err)) 74 | pass 75 | else: 76 | if event_sub_type == 'status': 77 | if self.received_status_notif: 78 | self.received_status_notif(parsed_notif) 79 | else: 80 | # dispatch 81 | self.dispatch('fromMote.' + event_sub_type, parsed_notif) 82 | 83 | # ======================== eventBus interaction ============================ 84 | 85 | def _info_dag_root_handler(self, sender, signal, data): 86 | 87 | # I only care about "infoDagRoot" notifications about my mote 88 | if not data['serialPort'] == self.serialport: 89 | return 90 | 91 | with self.state_lock: 92 | 93 | if data['isDAGroot'] == 1 and (not self._subscribed_data_for_dagroot): 94 | # this mote_connector is connected to a DAGroot 95 | 96 | # connect to dispatcher 97 | self.register(sender=self.WILDCARD, signal='bytesToMesh', callback=self._bytes_to_mesh_handler) 98 | 99 | # remember I'm subscribed 100 | self._subscribed_data_for_dagroot = True 101 | 102 | elif data['isDAGroot'] == 0 and self._subscribed_data_for_dagroot: 103 | # this mote_connector is *not* connected to a DAGroot 104 | 105 | # disconnect from dispatcher 106 | self.unregister(sender=self.WILDCARD, signal='bytesToMesh', callback=self._bytes_to_mesh_handler) 107 | 108 | # remember I'm not subscribed 109 | self._subscribed_data_for_dagroot = False 110 | 111 | def _cmd_to_mote_handler(self, sender, signal, data): 112 | if data['serialPort'] == self.serialport: 113 | if data['action'] == MoteState.TRIGGER_DAGROOT: 114 | 115 | # retrieve the prefix of the network 116 | with self.state_lock: 117 | if not self.network_prefix: 118 | network_prefix = self._dispatch_and_get_result(signal='getNetworkPrefix', data=[]) 119 | self.network_prefix = network_prefix 120 | 121 | # retrieve the security key of the network 122 | with self.state_lock: 123 | key_dict = self._dispatch_and_get_result(signal='getL2SecurityKey', data=[]) 124 | 125 | # create data to send 126 | with self.state_lock: 127 | data_to_send = [ 128 | openparser.OpenParser.SERFRAME_PC2MOTE_SETDAGROOT, 129 | openparser.OpenParser.SERFRAME_ACTION_TOGGLE, 130 | ] + self.network_prefix + key_dict['index'] + key_dict['value'] 131 | 132 | # toggle the DAGroot state 133 | self._send_to_mote_probe(data_to_send=data_to_send) 134 | else: 135 | raise SystemError('unexpected action={0}'.format(data['action'])) 136 | 137 | def _bytes_to_mesh_handler(self, sender, signal, data): 138 | assert type(data) == tuple 139 | assert len(data) == 2 140 | 141 | next_hop, lowpan = data 142 | 143 | self._send_to_mote_probe(data_to_send=[openparser.OpenParser.SERFRAME_PC2MOTE_DATA] + next_hop + lowpan) 144 | 145 | # ======================== public ========================================== 146 | 147 | def quit(self): 148 | raise NotImplementedError() 149 | 150 | # ======================== private ========================================= 151 | 152 | def _send_to_mote_probe(self, data_to_send): 153 | try: 154 | dispatcher.send( 155 | sender=self.name, 156 | signal='fromMoteConnector@' + self.serialport, 157 | data=''.join([chr(c) for c in data_to_send]), 158 | ) 159 | 160 | except socket.error as err: 161 | log.error(err) 162 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/motehandler/moteconnector/openparser/__init__.py -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/openparser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | 9 | from openvisualizer.motehandler.moteconnector.openparser import parser, parserstatus, parserdata, parserpacket, \ 10 | parserprintf 11 | from openvisualizer.motehandler.moteconnector.openparser.parserlogs import ParserLogs 12 | 13 | log = logging.getLogger('OpenParser') 14 | log.setLevel(logging.ERROR) 15 | log.addHandler(logging.NullHandler()) 16 | 17 | 18 | class OpenParser(parser.Parser): 19 | HEADER_LENGTH = 1 20 | 21 | SERFRAME_MOTE2PC_DATA = ord('D') 22 | SERFRAME_MOTE2PC_STATUS = ord('S') 23 | SERFRAME_MOTE2PC_VERBOSE = ParserLogs.LogSeverity.SEVERITY_VERBOSE 24 | SERFRAME_MOTE2PC_INFO = ParserLogs.LogSeverity.SEVERITY_INFO 25 | SERFRAME_MOTE2PC_WARNING = ParserLogs.LogSeverity.SEVERITY_WARNING 26 | SERFRAME_MOTE2PC_SUCCESS = ParserLogs.LogSeverity.SEVERITY_SUCCESS 27 | SERFRAME_MOTE2PC_ERROR = ParserLogs.LogSeverity.SEVERITY_ERROR 28 | SERFRAME_MOTE2PC_CRITICAL = ParserLogs.LogSeverity.SEVERITY_CRITICAL 29 | SERFRAME_MOTE2PC_SNIFFED_PACKET = ord('P') 30 | SERFRAME_MOTE2PC_PRINTF = ord('F') 31 | 32 | SERFRAME_PC2MOTE_SETDAGROOT = ord('R') 33 | SERFRAME_PC2MOTE_DATA = ord('D') 34 | SERFRAME_PC2MOTE_TRIGGERSERIALECHO = ord('S') 35 | SERFRAME_PC2MOTE_COMMAND = ord('C') 36 | 37 | SERFRAME_ACTION_YES = ord('Y') 38 | SERFRAME_ACTION_NO = ord('N') 39 | SERFRAME_ACTION_TOGGLE = ord('T') 40 | 41 | def __init__(self, mqtt_broker, stack_defines, mote_port): 42 | # log 43 | log.debug("create instance") 44 | 45 | # initialize parent class 46 | super(OpenParser, self).__init__(self.HEADER_LENGTH) 47 | 48 | # subparser objects 49 | self.parser_status = parserstatus.ParserStatus() 50 | self.parser_verbose = ParserLogs(self.SERFRAME_MOTE2PC_VERBOSE, stack_defines) 51 | self.parser_info = ParserLogs(self.SERFRAME_MOTE2PC_INFO, stack_defines) 52 | self.parser_warning = ParserLogs(self.SERFRAME_MOTE2PC_WARNING, stack_defines) 53 | self.parser_success = ParserLogs(self.SERFRAME_MOTE2PC_SUCCESS, stack_defines) 54 | self.parser_error = ParserLogs(self.SERFRAME_MOTE2PC_ERROR, stack_defines) 55 | self.parser_critical = ParserLogs(self.SERFRAME_MOTE2PC_CRITICAL, stack_defines) 56 | self.parser_data = parserdata.ParserData(mqtt_broker, mote_port) 57 | self.parser_packet = parserpacket.ParserPacket() 58 | self.parser_printf = parserprintf.ParserPrintf() 59 | 60 | # register subparsers 61 | self._add_sub_parser( 62 | index=0, 63 | val=self.SERFRAME_MOTE2PC_DATA, 64 | parser=self.parser_data, 65 | ) 66 | self._add_sub_parser( 67 | index=0, 68 | val=self.SERFRAME_MOTE2PC_STATUS, 69 | parser=self.parser_status, 70 | ) 71 | self._add_sub_parser( 72 | index=0, 73 | val=self.SERFRAME_MOTE2PC_VERBOSE, 74 | parser=self.parser_verbose, 75 | ) 76 | self._add_sub_parser( 77 | index=0, 78 | val=self.SERFRAME_MOTE2PC_INFO, 79 | parser=self.parser_info, 80 | ) 81 | self._add_sub_parser( 82 | index=0, 83 | val=self.SERFRAME_MOTE2PC_WARNING, 84 | parser=self.parser_warning, 85 | ) 86 | self._add_sub_parser( 87 | index=0, 88 | val=self.SERFRAME_MOTE2PC_SUCCESS, 89 | parser=self.parser_success, 90 | ) 91 | self._add_sub_parser( 92 | index=0, 93 | val=self.SERFRAME_MOTE2PC_ERROR, 94 | parser=self.parser_error, 95 | ) 96 | self._add_sub_parser( 97 | index=0, 98 | val=self.SERFRAME_MOTE2PC_CRITICAL, 99 | parser=self.parser_critical, 100 | ) 101 | self._add_sub_parser( 102 | index=0, 103 | val=self.SERFRAME_MOTE2PC_SNIFFED_PACKET, 104 | parser=self.parser_packet, 105 | ) 106 | self._add_sub_parser( 107 | index=0, 108 | val=self.SERFRAME_MOTE2PC_PRINTF, 109 | parser=self.parser_printf, 110 | ) 111 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/parser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2020, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | from abc import ABCMeta 9 | 10 | from openvisualizer.motehandler.moteconnector.openparser.parserexception import ParserException 11 | 12 | log = logging.getLogger('Parser') 13 | log.setLevel(logging.ERROR) 14 | log.addHandler(logging.NullHandler()) 15 | 16 | 17 | class ParsingKey(object): 18 | 19 | def __init__(self, index, val, parser): 20 | assert (index is not None) 21 | assert (val is not None) 22 | assert (parser is not None) 23 | 24 | self.index = index 25 | self.val = val 26 | self.parser = parser 27 | 28 | def __str__(self): 29 | template = "{0}={1}" 30 | output = [] 31 | output += [template.format("index", self.index)] 32 | output += [template.format("val", self.val)] 33 | output += [template.format("parser", self.parser)] 34 | return ' '.join(output) 35 | 36 | 37 | class Parser(object): 38 | __metaclass__ = ABCMeta 39 | 40 | def __init__(self, header_length): 41 | 42 | # store params 43 | self.header_length = header_length 44 | 45 | # local variables 46 | self.parsing_keys = [] 47 | self.header_parsing_keys = [] 48 | self.named_tuple = {} 49 | 50 | # ======================== public ========================================== 51 | 52 | def parse_input(self, data): 53 | 54 | # log 55 | log.debug("received data: {0}".format(data)) 56 | 57 | # ensure data not short longer than header 58 | self._check_length(data) 59 | 60 | # parse the header 61 | # TODO 62 | 63 | # call the next header parser 64 | for key in self.parsing_keys: 65 | if data[key.index] == key.val: 66 | return key.parser.parse_input(data[self.header_length:]) 67 | 68 | # if you get here, no key was found 69 | raise ParserException(ParserException.ExceptionType.NO_KEY, "type={0} (\"{1}\")".format(data[0], chr(data[0]))) 70 | 71 | # ======================== private ========================================= 72 | 73 | def _check_length(self, input): 74 | if len(input) < self.header_length: 75 | raise ParserException(ParserException.ExceptionType.TOO_SHORT) 76 | 77 | def _add_sub_parser(self, index=None, val=None, parser=None): 78 | self.parsing_keys.append(ParsingKey(index, val, parser)) 79 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/parserexception.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | from enum import Enum 8 | 9 | 10 | class ParserException(Exception): 11 | class ExceptionType(Enum): 12 | GENERIC = 1 13 | TOO_SHORT = 2 14 | WRONG_LENGTH = 3 15 | UNKNOWN_OPTION = 4 16 | NO_KEY = 5 17 | DESERIALIZE = 6 18 | 19 | descriptions = { 20 | ExceptionType.GENERIC: 'generic parsing error', 21 | ExceptionType.TOO_SHORT: 'data too short', 22 | ExceptionType.WRONG_LENGTH: 'data of the wrong length', 23 | ExceptionType.UNKNOWN_OPTION: 'no parser key', 24 | ExceptionType.NO_KEY: 'no key', 25 | ExceptionType.DESERIALIZE: 'deserialization error', 26 | } 27 | 28 | def __init__(self, error_code, details=None): 29 | self.error_code = error_code 30 | self.details = details 31 | 32 | def __str__(self): 33 | try: 34 | output = self.descriptions[self.error_code] 35 | if self.details: 36 | output += ': ' + str(self.details) 37 | return output 38 | except KeyError: 39 | return "Unknown error: #" + str(self.error_code) 40 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/parserlogs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import struct 9 | 10 | import verboselogs 11 | from enum import IntEnum 12 | 13 | from openvisualizer.motehandler.moteconnector.openparser.parser import Parser 14 | from openvisualizer.motehandler.moteconnector.openparser.parserexception import ParserException 15 | 16 | verboselogs.install() 17 | 18 | log = logging.getLogger('ParserLogs') 19 | log.setLevel(logging.ERROR) 20 | log.addHandler(logging.NullHandler()) 21 | 22 | 23 | class ParserLogs(Parser): 24 | HEADER_LENGTH = 1 25 | 26 | class LogSeverity(IntEnum): 27 | SEVERITY_VERBOSE = ord('V') 28 | SEVERITY_INFO = ord('I') 29 | SEVERITY_WARNING = ord('W') 30 | SEVERITY_SUCCESS = ord('U') 31 | SEVERITY_ERROR = ord('E') 32 | SEVERITY_CRITICAL = ord('C') 33 | 34 | def __init__(self, severity, stack_defines): 35 | assert self.LogSeverity(severity) 36 | 37 | # log 38 | log.debug("create instance") 39 | 40 | # initialize parent class 41 | super(ParserLogs, self).__init__(self.HEADER_LENGTH) 42 | 43 | # store params 44 | self.severity = severity 45 | self.stack_defines = stack_defines 46 | 47 | # store error info 48 | self.error_info = {} 49 | 50 | # ======================== public ========================================== 51 | 52 | def parse_input(self, data): 53 | 54 | # log 55 | log.debug("received data {0}".format(data)) 56 | 57 | # parse packet 58 | try: 59 | mote_id, component, error_code, arg1, arg2 = struct.unpack('>HBBhH', ''.join([chr(c) for c in data])) 60 | except struct.error: 61 | raise ParserException(ParserException.ExceptionType.DESERIALIZE.value, 62 | "could not extract data from {0}".format(data)) 63 | 64 | if (component, error_code) in self.error_info.keys(): 65 | self.error_info[(component, error_code)] += 1 66 | else: 67 | self.error_info[(component, error_code)] = 1 68 | 69 | if error_code == 0x25: 70 | # replace args of sixtop command/return code id by string 71 | arg1 = self.stack_defines["sixtop_returncodes"][arg1] 72 | arg2 = self.stack_defines["sixtop_states"][arg2] 73 | 74 | # turn into string 75 | output = "{MOTEID:x} [{COMPONENT}] {ERROR_DESC}".format( 76 | COMPONENT=self._translate_component(component), 77 | MOTEID=mote_id, 78 | ERROR_DESC=self._translate_log_description(error_code, arg1, arg2), 79 | ) 80 | 81 | # log 82 | if self.severity == self.LogSeverity.SEVERITY_VERBOSE: 83 | log.verbose(output) 84 | elif self.severity == self.LogSeverity.SEVERITY_INFO: 85 | log.info(output) 86 | elif self.severity == self.LogSeverity.SEVERITY_WARNING: 87 | log.warning(output) 88 | elif self.severity == self.LogSeverity.SEVERITY_SUCCESS: 89 | log.success(output) 90 | elif self.severity == self.LogSeverity.SEVERITY_ERROR: 91 | log.error(output) 92 | elif self.severity == self.LogSeverity.SEVERITY_CRITICAL: 93 | log.critical(output) 94 | else: 95 | raise SystemError("unexpected severity={0}".format(self.severity)) 96 | 97 | return 'error', data 98 | 99 | # ======================== private ========================================= 100 | 101 | def _translate_component(self, component): 102 | try: 103 | return self.stack_defines["components"][component] 104 | except KeyError: 105 | return "unknown component code {0}".format(component) 106 | 107 | def _translate_log_description(self, error_code, arg1, arg2): 108 | try: 109 | return self.stack_defines["log_descriptions"][error_code].format( 110 | arg1, arg2) 111 | except KeyError: 112 | return "unknown error {0} arg1={1} arg2={2}".format(error_code, arg1, arg2) 113 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/parserpacket.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | 9 | from openvisualizer.motehandler.moteconnector.openparser import parser 10 | 11 | log = logging.getLogger('ParserPacket') 12 | log.setLevel(logging.ERROR) 13 | log.addHandler(logging.NullHandler()) 14 | 15 | 16 | class ParserPacket(parser.Parser): 17 | HEADER_LENGTH = 2 18 | 19 | def __init__(self): 20 | # log 21 | log.debug("create instance") 22 | 23 | # initialize parent class 24 | super(ParserPacket, self).__init__(self.HEADER_LENGTH) 25 | 26 | # ======================== public ========================================== 27 | 28 | def parse_input(self, data): 29 | # log 30 | log.debug("received packet: {0}".format(data)) 31 | 32 | # ensure data not short longer than header 33 | self._check_length(data) 34 | 35 | _ = data[:2] # header bytes 36 | 37 | # remove mote id at the beginning. 38 | data = data[2:] 39 | 40 | log.debug("packet without header: {0}".format(data)) 41 | 42 | event_type = 'sniffedPacket' 43 | 44 | # notify a tuple including source as one hop away nodes elide SRC address as can be inferred from MAC layer 45 | # header 46 | return event_type, data 47 | 48 | # ======================== private ========================================= 49 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteconnector/openparser/parserprintf.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, CNRS. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import sys 9 | 10 | from openvisualizer.motehandler.moteconnector.openparser import parser 11 | 12 | log = logging.getLogger('ParserPrintf') 13 | log.setLevel(logging.INFO) 14 | log.addHandler(logging.NullHandler()) 15 | 16 | 17 | class ParserPrintf(parser.Parser): 18 | HEADER_LENGTH = 2 19 | 20 | def __init__(self): 21 | 22 | # log 23 | log.debug('create instance') 24 | 25 | # initialize parent class 26 | super(ParserPrintf, self).__init__(self.HEADER_LENGTH) 27 | 28 | # returns a string with the decimal value of a uint16_t 29 | @staticmethod 30 | def bytes_to_string(bytestring): 31 | string = '' 32 | i = 0 33 | 34 | for byte in bytestring: 35 | string = format(eval('{0} + {1} * 256 ** {2}'.format(string, byte, i))) 36 | i = i + 1 37 | 38 | return string 39 | 40 | @staticmethod 41 | def bytes_to_addr(bytestring): 42 | string = '' 43 | 44 | for byte in bytestring: 45 | string = string + '{:02x}'.format(byte) 46 | 47 | return string 48 | 49 | def parse_input(self, data): 50 | 51 | # log 52 | log.debug('received printf {0}'.format(data)) 53 | 54 | _ = ParserPrintf.bytes_to_addr(data[0:2]) # addr 55 | _ = ParserPrintf.bytes_to_string(data[2:7]) # asn 56 | 57 | sys.stdout.write("{}".format("".join([chr(c) for c in data[7:]]))) 58 | sys.stdout.flush() 59 | 60 | # everything was fine 61 | return 'error', data 62 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/motehandler/moteprobe/__init__.py -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/emulatedmoteprobe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | 9 | from moteprobe import MoteProbe 10 | 11 | log = logging.getLogger('MoteProbe') 12 | log.setLevel(logging.ERROR) 13 | log.addHandler(logging.NullHandler()) 14 | 15 | 16 | # ============================ functions =============================== 17 | 18 | 19 | # ============================ class =================================== 20 | 21 | class EmulatedMoteProbe(MoteProbe): 22 | def __init__(self, emulated_mote): 23 | self.emulated_mote = emulated_mote 24 | self._serial = None 25 | 26 | if not self.emulated_mote: 27 | raise SystemError() 28 | 29 | name = 'emulated{0}'.format(self.emulated_mote.get_id()) 30 | # initialize the parent class 31 | MoteProbe.__init__(self, portname=name, daemon=True) 32 | 33 | # ======================== private ================================= 34 | 35 | def _send_data(self, data): 36 | hdlc_data = self.hdlc.hdlcify(data) 37 | bytes_written = 0 38 | while bytes_written != len(bytearray(hdlc_data)): 39 | bytes_written += self.serial.write(hdlc_data) 40 | 41 | def _rcv_data(self): 42 | return self.serial.read() 43 | 44 | def _detach(self): 45 | pass 46 | 47 | @property 48 | def serial(self): 49 | return self._serial 50 | 51 | def _attach(self): 52 | self._serial = self.emulated_mote.bsp_uart 53 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/iotlabmoteprobe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import os 9 | import re 10 | import signal 11 | import socket 12 | import time 13 | from contextlib import closing 14 | 15 | import sshtunnel 16 | from iotlabcli import auth 17 | 18 | from moteprobe import MoteProbe, MoteProbeNoData 19 | 20 | log = logging.getLogger('MoteProbe') 21 | log.setLevel(logging.ERROR) 22 | log.addHandler(logging.NullHandler()) 23 | 24 | 25 | # ============================ class =================================== 26 | 27 | class IotlabMoteProbe(MoteProbe): 28 | IOTLAB_SSH_TIMEOUT = 2 # seconds 29 | IOTLAB_SOCKET_TIMEOUT = 2 # seconds 30 | IOTLAB_MOTE_TCP_PORT = 20000 31 | 32 | IOTLAB_FRONTEND_BASE_URL = 'iot-lab.info' 33 | 34 | def __init__(self, iotlab_mote, iotlab_user=None, iotlab_passwd=None): 35 | self.iotlab_mote = iotlab_mote 36 | 37 | if self.IOTLAB_FRONTEND_BASE_URL in self.iotlab_mote: 38 | # Recover user credentials 39 | self.iotlab_user, self.iotlab_passwd = auth.get_user_credentials(iotlab_user, iotlab_passwd) 40 | 41 | # match the site from the mote's address 42 | reg = r'[0-9a-zA-Z\-]+-\d+\.([a-z]+)' 43 | match = re.search(reg, iotlab_mote) 44 | self.iotlab_site = match.group(1) 45 | 46 | self.iotlab_tunnel = None 47 | self.socket = None 48 | 49 | # initialize the parent class 50 | MoteProbe.__init__(self, portname=iotlab_mote) 51 | 52 | # ======================== public ================================== 53 | 54 | @classmethod 55 | def probe_iotlab_motes(cls, iotlab_motes, iotlab_user, iotlab_passwd): 56 | mote_probes = [] 57 | probe = None 58 | log.debug("probing motes: {}".format(iotlab_motes)) 59 | try: 60 | for mote in iotlab_motes: 61 | log.debug("probe {}".format(mote)) 62 | try: 63 | probe = cls( 64 | iotlab_mote=mote, 65 | iotlab_user=iotlab_user, 66 | iotlab_passwd=iotlab_passwd) 67 | while probe.socket is None and probe.isAlive(): 68 | pass 69 | if probe.test_serial(pkts=2): 70 | log.success("{} Ok.".format(probe._portname)) 71 | mote_probes.append(probe) 72 | else: 73 | # Exit unresponsive moteprobe threads 74 | probe.close() 75 | probe.join() 76 | except Exception as e: 77 | if probe: 78 | probe.close() 79 | probe.join() 80 | log.error(e) 81 | except KeyboardInterrupt: 82 | # graceful exit 83 | for mote in mote_probes: 84 | mote.close() 85 | mote.join() 86 | if probe: 87 | probe.close() 88 | probe.join() 89 | os.kill(os.getpid(), signal.SIGTERM) 90 | 91 | valid_motes = ['{0}'.format(p._portname) for p in mote_probes] 92 | log.success("discovered following iotlab-motes: {}".format(valid_motes)) 93 | 94 | return mote_probes 95 | 96 | @property 97 | def serial(self): 98 | return self.socket 99 | 100 | # ======================== private ================================= 101 | 102 | @staticmethod 103 | def _get_free_port(): 104 | with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: 105 | s.bind(('', 0)) 106 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 107 | return s.getsockname()[1] 108 | 109 | def _rcv_data(self, rx_bytes=1024): 110 | try: 111 | return self.socket.recv(rx_bytes) 112 | except socket.timeout: 113 | raise MoteProbeNoData 114 | 115 | def _send_data(self, data): 116 | hdlc_data = self.hdlc.hdlcify(data) 117 | self.socket.send(hdlc_data) 118 | 119 | def _detach(self): 120 | if self.socket is not None: 121 | log.debug('closing socket to {}'.format(self._portname)) 122 | self.socket.close() 123 | 124 | if self.iotlab_tunnel is not None: 125 | log.debug('stopping ssh tunnel to {}'.format(self._portname)) 126 | self.iotlab_tunnel.stop() 127 | 128 | def _attach(self): 129 | if hasattr(self, 'iotlab_site'): 130 | port = self._get_free_port() 131 | sshtunnel.SSH_TIMEOUT = self.IOTLAB_SSH_TIMEOUT 132 | self.iotlab_tunnel = sshtunnel.open_tunnel('{}.{}'.format(self.iotlab_site, self.IOTLAB_FRONTEND_BASE_URL), 133 | ssh_username=self.iotlab_user, 134 | ssh_password=self.iotlab_passwd, 135 | remote_bind_address=( 136 | self.iotlab_mote, self.IOTLAB_MOTE_TCP_PORT), 137 | local_bind_address=('0.0.0.0', port)) 138 | self.iotlab_tunnel.start() 139 | time.sleep(0.1) 140 | 141 | log.debug('{}: ssh tunnel started'.format(self.iotlab_mote)) 142 | 143 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 144 | self.socket.settimeout(self.IOTLAB_SOCKET_TIMEOUT) 145 | self.socket.connect(('127.0.0.1', port)) 146 | 147 | log.debug('{}: socket connected'.format(self.iotlab_mote)) 148 | else: 149 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 150 | self.socket.connect((self.iotlab_mote, self.IOTLAB_MOTE_TCP_PORT)) 151 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/mockmoteprobe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | from moteprobe import MoteProbe 8 | 9 | 10 | # ============================ class =================================== 11 | 12 | class MockMoteProbe(MoteProbe): 13 | def __init__(self, mock_name, daemon=False, buffer=None): 14 | 15 | self.trigger_rcv = False 16 | self._blocking = False 17 | self._buffer = buffer 18 | 19 | # initialize the parent class 20 | MoteProbe.__init__(self, portname=mock_name, daemon=daemon) 21 | 22 | self.send_to_parser = self.receive_data_from_mote_probe 23 | self.send_to_parser_data = None 24 | 25 | @property 26 | def buffer(self): 27 | with self.data_lock: 28 | return self._buffer 29 | 30 | @buffer.setter 31 | def buffer(self, value): 32 | with self.data_lock: 33 | self._buffer = value 34 | 35 | @property 36 | def serial(self): 37 | return None 38 | 39 | @property 40 | def blocking(self): 41 | with self.data_lock: 42 | return self._blocking 43 | 44 | @blocking.setter 45 | def blocking(self, value): 46 | with self.data_lock: 47 | self._blocking = value 48 | 49 | # ======================== public ================================= 50 | 51 | def receive_data_from_mote_probe(self, data): 52 | self.send_to_parser_data = data 53 | 54 | # ======================== private ================================= 55 | 56 | def _send_data(self, data): 57 | pass 58 | 59 | def _rcv_data(self): 60 | if self.quit: 61 | return '0x00' 62 | else: 63 | while not self.quit and (self.blocking or self.buffer is None): 64 | pass 65 | if self.buffer: 66 | tmp_buffer = self.buffer 67 | self.buffer = None 68 | return tmp_buffer 69 | else: 70 | return '0x00' 71 | 72 | def _detach(self): 73 | pass 74 | 75 | def _attach(self): 76 | pass 77 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/openhdlc.py: -------------------------------------------------------------------------------- 1 | """ 2 | HDLC framing module. 3 | 4 | .. moduleauthor:: Min Ting 5 | October 2012 6 | """ 7 | 8 | import logging 9 | 10 | from openvisualizer.utils import format_string_buf 11 | 12 | log = logging.getLogger('OpenHdlc') 13 | log.setLevel(logging.ERROR) 14 | log.addHandler(logging.NullHandler()) 15 | 16 | 17 | class HdlcException(Exception): 18 | pass 19 | 20 | 21 | class OpenHdlc(object): 22 | HDLC_FLAG = '\x7e' 23 | HDLC_FLAG_ESCAPED = '\x5e' 24 | HDLC_ESCAPE = '\x7d' 25 | HDLC_ESCAPE_ESCAPED = '\x5d' 26 | HDLC_CRCINIT = 0xffff 27 | HDLC_CRCGOOD = 0xf0b8 28 | 29 | FCS16TAB = ( 30 | 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 31 | 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 32 | 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 33 | 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 34 | 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 35 | 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 36 | 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 37 | 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 38 | 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 39 | 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 40 | 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 41 | 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 42 | 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 43 | 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 44 | 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 45 | 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 46 | 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 47 | 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 48 | 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 49 | 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 50 | 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 51 | 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 52 | 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 53 | 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 54 | 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 55 | 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 56 | 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 57 | 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 58 | 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 59 | 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 60 | 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 61 | 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, 62 | ) 63 | 64 | # ============================ public ====================================== 65 | 66 | def hdlcify(self, in_buf): 67 | """ 68 | Build an hdlc frame. 69 | 70 | Use 0x00 for both addr byte, and control byte. 71 | """ 72 | 73 | # make copy of input 74 | out_buf = in_buf[:] 75 | 76 | # calculate CRC 77 | crc = self.HDLC_CRCINIT 78 | for b in out_buf: 79 | crc = self._crc_iteration(crc, b) 80 | crc = 0xffff - crc 81 | 82 | # append CRC 83 | out_buf = out_buf + chr(crc & 0xff) + chr((crc & 0xff00) >> 8) 84 | 85 | # stuff bytes 86 | out_buf = out_buf.replace(self.HDLC_ESCAPE, self.HDLC_ESCAPE + self.HDLC_ESCAPE_ESCAPED) 87 | out_buf = out_buf.replace(self.HDLC_FLAG, self.HDLC_ESCAPE + self.HDLC_FLAG_ESCAPED) 88 | 89 | # add flags 90 | out_buf = self.HDLC_FLAG + out_buf + self.HDLC_FLAG 91 | 92 | return out_buf 93 | 94 | def dehdlcify(self, in_buf): 95 | """ 96 | Parse an hdlc frame. 97 | 98 | :returns: the extracted frame, or -1 if wrong checksum 99 | """ 100 | assert in_buf[0] == self.HDLC_FLAG 101 | assert in_buf[-1] == self.HDLC_FLAG 102 | 103 | # make copy of input 104 | out_buf = in_buf[:] 105 | if log.isEnabledFor(logging.DEBUG): 106 | log.debug("got {0}".format(format_string_buf(out_buf))) 107 | 108 | # remove flags 109 | out_buf = out_buf[1:-1] 110 | if log.isEnabledFor(logging.DEBUG): 111 | log.debug("after flags: {0}".format(format_string_buf(out_buf))) 112 | 113 | # unstuff 114 | out_buf = out_buf.replace(self.HDLC_ESCAPE + self.HDLC_FLAG_ESCAPED, self.HDLC_FLAG) 115 | out_buf = out_buf.replace(self.HDLC_ESCAPE + self.HDLC_ESCAPE_ESCAPED, self.HDLC_ESCAPE) 116 | if log.isEnabledFor(logging.DEBUG): 117 | log.debug("after unstuff: {0}".format(format_string_buf(out_buf))) 118 | 119 | if len(out_buf) < 2: 120 | raise HdlcException('packet too short') 121 | 122 | # check CRC 123 | crc = self.HDLC_CRCINIT 124 | for b in out_buf: 125 | crc = self._crc_iteration(crc, b) 126 | if crc != self.HDLC_CRCGOOD: 127 | raise HdlcException('wrong CRC') 128 | 129 | # remove CRC 130 | out_buf = out_buf[:-2] # remove CRC 131 | if log.isEnabledFor(logging.DEBUG): 132 | log.debug("after CRC: {0}".format(format_string_buf(out_buf))) 133 | 134 | return out_buf 135 | 136 | # ============================ private ===================================== 137 | 138 | def _crc_iteration(self, crc, b): 139 | return (crc >> 8) ^ self.FCS16TAB[((crc ^ (ord(b))) & 0xff)] 140 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/serialmoteprobe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | import os 9 | import signal 10 | 11 | import serial 12 | 13 | from moteprobe import MoteProbe, MoteProbeNoData 14 | 15 | try: 16 | import _winreg as winreg 17 | except ImportError: 18 | import glob 19 | import platform 20 | 21 | log = logging.getLogger('MoteProbe') 22 | log.setLevel(logging.ERROR) 23 | log.addHandler(logging.NullHandler()) 24 | 25 | 26 | # ============================ class =================================== 27 | 28 | class SerialMoteProbe(MoteProbe): 29 | def __init__(self, port, baudrate): 30 | self._port = port 31 | self._baudrate = baudrate 32 | self._serial = None 33 | 34 | # initialize the parent class 35 | MoteProbe.__init__(self, portname=port) 36 | 37 | # ======================== public ================================== 38 | 39 | @property 40 | def baudrate(self): 41 | with self.data_lock: 42 | return self._baudrate 43 | 44 | @property 45 | def serial(self): 46 | return self._serial 47 | 48 | @classmethod 49 | def probe_serial_ports(cls, baudrate, port_mask=None): 50 | ports = cls._get_ports_from_mask(port_mask) 51 | mote_probes = [] 52 | probe = None 53 | 54 | log.warning("Probing motes: {} at baudrates {}".format(ports, baudrate)) 55 | 56 | try: 57 | for port in ports: 58 | try: 59 | probe = cls(port=port, baudrate=115200) 60 | while probe._serial is None: 61 | pass 62 | for baud in baudrate: 63 | log.debug("Probe port {} at baudrate {}".format(port, baud)) 64 | probe._serial.baudrate = baud 65 | if probe.test_serial(pkts=2): 66 | mote_probes.append(probe) 67 | break 68 | except Exception as e: 69 | if probe: 70 | probe.close() 71 | probe.join() 72 | log.error(e) 73 | except KeyboardInterrupt: 74 | # graceful exit 75 | for mote in mote_probes: 76 | mote.close() 77 | mote.join() 78 | if probe: 79 | probe.close() 80 | probe.join() 81 | os.kill(os.getpid(), signal.SIGTERM) 82 | valid_motes = ['{0}'.format(p._portname) for p in mote_probes] 83 | log.success("Discovered serial-port(s): {0}".format(valid_motes)) 84 | 85 | return mote_probes 86 | 87 | # ======================== private ================================= 88 | 89 | def _send_data(self, data): 90 | hdlc_data = self.hdlc.hdlcify(data) 91 | bytes_written = 0 92 | self._serial.flush() 93 | while bytes_written != len(bytearray(hdlc_data)): 94 | bytes_written += self._serial.write(hdlc_data) 95 | 96 | def _rcv_data(self, rx_bytes=1): 97 | data = self._serial.read(rx_bytes) 98 | if data == 0: 99 | raise MoteProbeNoData 100 | else: 101 | return data 102 | 103 | def _detach(self): 104 | if self._serial is not None: 105 | log.warning('closing serial port {}'.format(self._portname)) 106 | self._serial.close() 107 | 108 | def _attach(self): 109 | log.debug("attaching to serial port: {} @ {}".format(self._port, self._baudrate)) 110 | self._serial = serial.Serial(self._port, self._baudrate, timeout=1, xonxoff=True, rtscts=False, dsrdtr=False) 111 | log.debug("self._serial: {}".format(self._serial)) 112 | 113 | @staticmethod 114 | def _get_ports_from_mask(port_mask=None): 115 | ports = [] 116 | 117 | if port_mask is None: 118 | if os.name == 'nt': 119 | path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM' 120 | try: 121 | key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path) 122 | for i in range(winreg.QueryInfoKey(key)[1]): 123 | try: 124 | val = winreg.EnumValue(key, i) 125 | except WindowsError: 126 | pass 127 | else: 128 | ports.append(str(val[1])) 129 | except WindowsError: 130 | pass 131 | elif os.name == 'posix': 132 | if platform.system() == 'Darwin': 133 | port_mask = ['/dev/tty.usbserial-*'] 134 | else: 135 | port_mask = ['/dev/ttyUSB*'] 136 | for mask in port_mask: 137 | ports += [s for s in glob.glob(mask)] 138 | else: 139 | for mask in port_mask: 140 | ports += [s for s in glob.glob(mask)] 141 | 142 | return ports 143 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/moteprobe/testbedmoteprobe.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import Queue 8 | import json 9 | import logging 10 | import time 11 | 12 | import paho.mqtt.client as mqtt 13 | 14 | from moteprobe import MoteProbe 15 | 16 | log = logging.getLogger('MoteProbe') 17 | log.setLevel(logging.ERROR) 18 | log.addHandler(logging.NullHandler()) 19 | 20 | 21 | # ============================ functions =============================== 22 | 23 | 24 | # ============================ class =================================== 25 | 26 | class OpentestbedMoteProbe(MoteProbe): 27 | BASE_TOPIC = 'opentestbed/deviceType/mote/deviceId' 28 | 29 | def __init__(self, mqtt_broker, testbedmote_eui64): 30 | self.mqtt_broker = mqtt_broker 31 | self.testbedmote_eui64 = testbedmote_eui64 32 | 33 | # mqtt client 34 | self.mqtt_client = mqtt.Client() 35 | self.mqtt_client.on_connect = self._on_mqtt_connect 36 | self.mqtt_client.on_message = self._on_mqtt_message 37 | self.mqtt_client.connect(self.mqtt_broker) 38 | # self.mqtt_client.on_log = self.log_cb 39 | 40 | name = 'opentestbed_{0}'.format(testbedmote_eui64) 41 | # initialize the parent class 42 | MoteProbe.__init__(self, portname=name, daemon=True) 43 | 44 | @property 45 | def serial(self): 46 | return None 47 | 48 | @staticmethod 49 | def log_cb(client, userdata, level, buf): 50 | _, _, _ = client, userdata, level 51 | log.info(buf) 52 | 53 | # ======================== private ================================= 54 | 55 | def _send_data(self, data): 56 | hdlc_data = self.hdlc.hdlcify(data) 57 | payload_buffer = {'token': 123, 'serialbytes': [ord(i) for i in hdlc_data]} 58 | 59 | # publish the cmd message 60 | self.mqtt_client.publish( 61 | topic='{}/{}/cmd/tomoteserialbytes'.format(self.BASE_TOPIC, self.testbedmote_eui64), 62 | payload=json.dumps(payload_buffer), 63 | ) 64 | 65 | def _rcv_data(self): 66 | rx_bytes = self.mqtt_serial_queue.get() 67 | return [chr(i) for i in rx_bytes] 68 | 69 | def _detach(self): 70 | pass 71 | 72 | def _attach(self): 73 | # create queue for receiving serialbytes messages 74 | self.serialbytes_queue = Queue.Queue(maxsize=10) 75 | 76 | self.mqtt_client.loop_start() 77 | 78 | self.mqtt_serial_queue = self.serialbytes_queue 79 | 80 | # ==== mqtt callback functions ===================================== 81 | 82 | def _on_mqtt_connect(self, client, userdata, flags, rc): 83 | client.subscribe('{}/{}/notif/frommoteserialbytes'.format(self.BASE_TOPIC, self.testbedmote_eui64)) 84 | 85 | def _on_mqtt_message(self, client, userdata, message): 86 | try: 87 | serial_bytes = json.loads(message.payload)['serialbytes'] 88 | except json.JSONDecodeError: 89 | log.error("failed to parse message payload {}".format(message.payload)) 90 | else: 91 | try: 92 | self.serialbytes_queue.put(serial_bytes, block=False) 93 | except Queue.Full: 94 | log.warning("queue overflow/full") 95 | 96 | 97 | # ============================ class =========================================== 98 | 99 | class OpentestbedMoteFinder(object): 100 | OPENTESTBED_RESP_STATUS_TIMEOUT = 10 101 | 102 | def __init__(self, mqtt_broker): 103 | self.opentestbed_motelist = set() 104 | self.mqtt_broker = mqtt_broker 105 | 106 | # create mqtt client 107 | self.mqtt_client = mqtt.Client('FindMotes') 108 | self.mqtt_client.on_connect = self._on_mqtt_connect 109 | self.mqtt_client.on_message = self._on_mqtt_message 110 | self.mqtt_client.connect(self.mqtt_broker) 111 | # self.mqtt_client.on_log = log_cb 112 | 113 | def get_opentestbed_motelist(self): 114 | # start the mqtt client 115 | self.mqtt_client.loop_start() 116 | 117 | # wait for a while to gather the response from otboxes 118 | log.info("discovering motes in testbed... (waiting for {}s)".format(self.OPENTESTBED_RESP_STATUS_TIMEOUT)) 119 | time.sleep(self.OPENTESTBED_RESP_STATUS_TIMEOUT) 120 | 121 | # close the client and return the motes list 122 | self.mqtt_client.loop_stop() 123 | 124 | log.info("discovered {0} motes".format(len(self.opentestbed_motelist))) 125 | 126 | return self.opentestbed_motelist 127 | 128 | @staticmethod 129 | def log_cb(client, userdata, level, buf): 130 | log.info(buf) 131 | 132 | def _on_mqtt_connect(self, client, userdata, flags, rc): 133 | 134 | log.success("connected to broker: {0}".format(self.mqtt_broker)) 135 | 136 | client.subscribe('opentestbed/deviceType/box/deviceId/+/resp/status') 137 | 138 | payload_status = {'token': 123} 139 | # publish the cmd message 140 | client.publish(topic='opentestbed/deviceType/box/deviceId/all/cmd/status', payload=json.dumps(payload_status)) 141 | 142 | def _on_mqtt_message(self, client, userdata, message): 143 | 144 | # get the motes list from payload 145 | payload_status = json.loads(message.payload) 146 | 147 | for mote in payload_status['returnVal']['motes']: 148 | if 'EUI64' in mote: 149 | self.opentestbed_motelist.add(mote['EUI64']) 150 | -------------------------------------------------------------------------------- /openvisualizer/motehandler/motestate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/motehandler/motestate/__init__.py -------------------------------------------------------------------------------- /openvisualizer/openlbr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/openlbr/__init__.py -------------------------------------------------------------------------------- /openvisualizer/openlbr/sixlowpan_frag.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import logging 8 | 9 | from openvisualizer.utils import buf2int, hex2buf 10 | 11 | log = logging.getLogger('SixLowPanFrag') 12 | log.setLevel(logging.INFO) 13 | log.addHandler(logging.NullHandler()) 14 | 15 | 16 | # ============================ parameters ====================================== 17 | 18 | class ReassembleEntry(object): 19 | def __init__(self, wanted, received, frag): 20 | self.total_bytes = wanted 21 | self.recvd_bytes = received 22 | self.fragments = frag 23 | 24 | 25 | class Fragmentor(object): 26 | """ 27 | Class which performs fragmentation and reassembly of 6LoWPAN packets for transport of IEEE 802.15.4 networks. 28 | 29 | This class implements the following RFCs; 30 | 31 | * *https://tools.ietf.org/html/rfc4944* 32 | Transmission of IPv6 Packets over IEEE 802.15.4 Networks. 33 | """ 34 | 35 | FRAG1_DISPATCH = 0xC0 36 | FRAGN_DISPATCH = 0xE0 37 | 38 | FRAG_DISPATCH_MASK = 0xF8 39 | FRAG_SIZE_MASK = 0x7FF 40 | 41 | # If L2 security is not active in the network we can use up to 96 bytes of payload per fragment. 42 | # Since openvisualizer is not aware of the security configuration of the network, we use by default a smaller 43 | # fragment payload size. 44 | MAX_FRAGMENT_SIZE = 80 45 | FRAG1_HDR_SIZE = 4 46 | FRAGN_HDR_SIZE = 5 47 | 48 | def __init__(self, tag=1): 49 | self.reassemble_buffer = dict() 50 | 51 | self.datagram_tag = tag 52 | 53 | def do_reassemble(self, lowpan_pkt): 54 | reassembled_pkt = None 55 | 56 | # parse fragmentation header 57 | dispatch = lowpan_pkt[0] & self.FRAG_DISPATCH_MASK 58 | datagram_size = buf2int(lowpan_pkt[:2]) & self.FRAG_SIZE_MASK 59 | 60 | if dispatch not in [self.FRAG1_DISPATCH, self.FRAGN_DISPATCH]: 61 | return lowpan_pkt 62 | 63 | # extract fragmentation tag 64 | datagram_tag = buf2int(lowpan_pkt[2:4]) 65 | 66 | if dispatch == self.FRAG1_DISPATCH: 67 | payload = lowpan_pkt[4:] 68 | offset = 0 69 | else: 70 | payload = lowpan_pkt[5:] 71 | offset = lowpan_pkt[4] 72 | 73 | if datagram_tag in self.reassemble_buffer: 74 | entry = self.reassemble_buffer[datagram_tag] 75 | entry.recvd_bytes += len(payload) 76 | entry.fragments.append((offset, payload)) 77 | else: 78 | new_entry = ReassembleEntry(datagram_size, len(payload), [(offset, payload)]) 79 | self.reassemble_buffer[datagram_tag] = new_entry 80 | 81 | # check if we can reassemble 82 | num_of_frags = 0 83 | used_tag = None 84 | for tag, entry in self.reassemble_buffer.items(): 85 | if entry.total_bytes == entry.recvd_bytes: 86 | frags = sorted(entry.fragments, key=lambda frag: frag[0]) 87 | used_tag = tag 88 | num_of_frags = len(frags) 89 | reassembled_pkt = [] 90 | 91 | for frag in frags: 92 | reassembled_pkt.extend(frag[1]) 93 | break 94 | 95 | if used_tag is not None: 96 | del self.reassemble_buffer[used_tag] 97 | 98 | if reassembled_pkt is not None: 99 | log.success("[GATEWAY] Reassembled {} frags with tag {} into an IPv6 packet of size {}".format( 100 | num_of_frags, used_tag, len(reassembled_pkt))) 101 | 102 | return reassembled_pkt 103 | 104 | def do_fragment(self, ip6_pkt): 105 | fragment_list = [] 106 | original_length = len(ip6_pkt) 107 | 108 | if len(ip6_pkt) <= self.MAX_FRAGMENT_SIZE + self.FRAGN_HDR_SIZE: 109 | return [ip6_pkt] 110 | 111 | while len(ip6_pkt) > 0: 112 | frag_header = [] 113 | fragment = [] 114 | 115 | datagram_tag = hex2buf("{:04x}".format(self.datagram_tag)) 116 | 117 | if len(ip6_pkt) > self.MAX_FRAGMENT_SIZE: 118 | frag_len = self.MAX_FRAGMENT_SIZE 119 | else: 120 | frag_len = len(ip6_pkt) 121 | 122 | if len(fragment_list) == 0: 123 | # first fragment 124 | dispatch_size = hex2buf("{:02x}".format((self.FRAG1_DISPATCH << 8) | original_length)) 125 | frag_header.extend(dispatch_size) 126 | frag_header.extend(datagram_tag) 127 | else: 128 | # subsequent fragment 129 | dispatch_size = hex2buf("{:02x}".format((self.FRAGN_DISPATCH << 8) | original_length)) 130 | offset = [len(fragment_list) * (self.MAX_FRAGMENT_SIZE / 8)] 131 | frag_header.extend(dispatch_size) 132 | frag_header.extend(datagram_tag) 133 | frag_header.extend(offset) 134 | 135 | fragment.extend(frag_header) 136 | fragment.extend(ip6_pkt[:frag_len]) 137 | 138 | fragment_list.append(fragment) 139 | 140 | ip6_pkt = ip6_pkt[frag_len:] 141 | 142 | # increment the tag for the new set of fragments 143 | self.datagram_tag += 1 144 | 145 | log.info("[GATEWAY] Fragmenting incoming IPv6 packet (size: {}) into {} fragments with tag {}".format( 146 | original_length, len(fragment_list), self.datagram_tag - 1)) 147 | 148 | return fragment_list 149 | -------------------------------------------------------------------------------- /openvisualizer/opentun/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import opentunnull # noqa: F401 4 | 5 | if sys.platform.startswith('win32'): 6 | import opentunwindows # noqa: F401 7 | 8 | if sys.platform.startswith('linux'): 9 | import opentunlinux # noqa: F401 10 | 11 | if sys.platform.startswith('darwin'): 12 | import opentunmacos # noqa: F401 13 | -------------------------------------------------------------------------------- /openvisualizer/opentun/opentun.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | import abc 8 | import logging 9 | import socket 10 | import sys 11 | import time 12 | 13 | import verboselogs 14 | 15 | from openvisualizer.eventbus.eventbusclient import EventBusClient 16 | from openvisualizer.utils import format_ipv6_addr 17 | 18 | verboselogs.install() 19 | 20 | log = logging.getLogger('OpenTun') 21 | log.setLevel(logging.ERROR) 22 | log.addHandler(logging.NullHandler()) 23 | 24 | 25 | class OpenTun(EventBusClient): 26 | """ 27 | Class which interfaces between a TUN virtual interface and an EventBus. 28 | This class is abstract, with concrete subclasses based on operating system. 29 | """ 30 | __metaclass__ = abc.ABCMeta 31 | 32 | # dynamically records supported operating systems 33 | os_support = {} 34 | 35 | # IPv6 address for TUN interface 36 | IPV6PREFIX = [0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] 37 | IPV6HOST = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01] 38 | 39 | def __init__(self): 40 | # register to receive outgoing network packets 41 | super(OpenTun, self).__init__( 42 | name='OpenTun', 43 | registrations=[ 44 | { 45 | 'sender': self.WILDCARD, 46 | 'signal': 'getNetworkPrefix', 47 | 'callback': self._get_network_prefix_notif, 48 | }, 49 | { 50 | 'sender': self.WILDCARD, 51 | 'signal': 'getNetworkHost', 52 | 'callback': self._get_network_host_notif, 53 | }, 54 | { 55 | 'sender': self.WILDCARD, 56 | 'signal': 'v6ToInternet', 57 | 'callback': self._v6_to_internet_notif, 58 | }, 59 | ], 60 | ) 61 | 62 | # local variables 63 | self.tun_if = self._create_tun_if() 64 | if self.tun_if: 65 | self.tun_read_thread = self._create_tun_read_thread() 66 | else: 67 | self.tun_read_thread = None 68 | 69 | # TODO: retrieve network prefix from interface settings 70 | 71 | # announce network prefix 72 | self.dispatch(signal='networkPrefix', data=self.IPV6PREFIX) 73 | 74 | # ======================== public ========================================== 75 | 76 | @classmethod 77 | def record_os(cls, os_id): 78 | """Decorator to record all the operating systems dynamically""" 79 | 80 | def decorator(the_class): 81 | if not issubclass(the_class, OpenTun): 82 | raise ValueError("Can only decorate subclass of OpenTun") 83 | cls.os_support[os_id] = the_class 84 | return the_class 85 | 86 | return decorator 87 | 88 | @classmethod 89 | def create(cls, opentun=False): 90 | """ Module-based Factory method to create instance based on operating system. """ 91 | 92 | if not opentun: 93 | return cls.os_support['null']() 94 | 95 | elif sys.platform.startswith('win32'): 96 | return cls.os_support['win32']() 97 | 98 | elif sys.platform.startswith('linux'): 99 | return cls.os_support['linux']() 100 | 101 | elif sys.platform.startswith('darwin'): 102 | return cls.os_support['darwin']() 103 | 104 | else: 105 | raise NotImplementedError('Platform {0} not supported'.format(sys.platform)) 106 | 107 | def close(self): 108 | 109 | if self.tun_read_thread: 110 | 111 | self.tun_read_thread.close() 112 | 113 | # Send a packet to OpenTun interface to break out of blocking read. 114 | attempts = 0 115 | while self.tun_read_thread.isAlive() and attempts < 3: 116 | attempts += 1 117 | try: 118 | log.info('Closing tun interface') 119 | log.debug('Sending UDP packet to close OpenTun') 120 | sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 121 | # Destination must route through the TUN host, but not be the host itself. 122 | # OK if host does not really exist. 123 | dst = self.IPV6PREFIX + self.IPV6HOST 124 | dst[15] += 1 125 | # Payload and destination port are arbitrary 126 | sock.sendto('stop', (format_ipv6_addr(dst), 18004)) 127 | # Give thread some time to exit 128 | time.sleep(0.05) 129 | except Exception as err: 130 | log.error('Unable to send UDP to close tun_read_thread: {0}'.format(str(err))) 131 | 132 | # ======================== private ========================================= 133 | 134 | def _get_network_prefix_notif(self, sender, signal, data): 135 | return self.IPV6PREFIX 136 | 137 | def _get_network_host_notif(self, sender, signal, data): 138 | return self.IPV6HOST 139 | 140 | def _v6_to_mesh_notif(self, data): 141 | """ 142 | Called when receiving data from the TUN interface. 143 | This function forwards the data to the the EventBus. Read from 6lowPAN and forward to TUN interface 144 | """ 145 | 146 | # dispatch to EventBus 147 | self.dispatch(signal='v6ToMesh', data=data) 148 | 149 | @abc.abstractmethod 150 | def _create_tun_if(self): 151 | """ 152 | Open a TUN/TAP interface and switch it to TUN mode. 153 | :returns: The handler of the interface, which can be used for later read/write operations. 154 | """ 155 | 156 | raise NotImplementedError('subclass must implement') 157 | 158 | @abc.abstractmethod 159 | def _create_tun_read_thread(self): 160 | """ Creates the thread to read messages arriving from the TUN interface """ 161 | raise NotImplementedError('subclass must implement') 162 | 163 | @abc.abstractmethod 164 | def _v6_to_internet_notif(self, sender, signal, data): 165 | """ 166 | Called when receiving data from the EventBus. 167 | 168 | This function forwards the data to the the TUN interface. Read from tun interface and forward to 6lowPAN 169 | """ 170 | raise NotImplementedError('subclass must implement') 171 | -------------------------------------------------------------------------------- /openvisualizer/opentun/opentunnull.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from openvisualizer.opentun.opentun import OpenTun 4 | 5 | log = logging.getLogger("OpenTunNull") 6 | log.setLevel(logging.ERROR) 7 | log.addHandler(logging.NullHandler()) 8 | 9 | 10 | @OpenTun.record_os('null') 11 | class OpenTunNull(OpenTun): 12 | def __init__(self): 13 | super(OpenTunNull, self).__init__() 14 | 15 | def _create_tun_read_thread(self): 16 | raise NotImplementedError() 17 | 18 | def _create_tun_if(self): 19 | return None 20 | 21 | def _v6_to_internet_notif(self, sender, signal, data): 22 | """ This method is called when OpenVisualizer can't route the packet. When OpenTunNull, just ignore. """ 23 | log.warning("dropping packet routed to the internet with OpenTunNull") 24 | -------------------------------------------------------------------------------- /openvisualizer/rpl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/rpl/__init__.py -------------------------------------------------------------------------------- /openvisualizer/rpl/sourceroute.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | """ 8 | Module which receives DAO messages and calculates source routes. 9 | 10 | .. moduleauthor:: Xavi Vilajosana 11 | January 2013 12 | .. moduleauthor:: Thomas Watteyne 13 | April 2013 14 | """ 15 | 16 | import logging 17 | import threading 18 | 19 | from openvisualizer.eventbus.eventbusclient import EventBusClient 20 | 21 | log = logging.getLogger('SourceRoute') 22 | log.setLevel(logging.ERROR) 23 | log.addHandler(logging.NullHandler()) 24 | 25 | 26 | class SourceRoute(EventBusClient): 27 | 28 | def __init__(self): 29 | 30 | # local variables 31 | self.dataLock = threading.Lock() 32 | self.parents = {} 33 | 34 | # initialize parent class 35 | super(SourceRoute, self).__init__(name='SourceRoute', registrations=[]) 36 | 37 | # ======================== public ========================================== 38 | 39 | def get_source_route(self, dest_addr): 40 | """ 41 | Retrieve the source route to a given mote. 42 | 43 | :param dest_addr: [in] The EUI64 address of the final destination. 44 | 45 | :returns: The source route, a list of EUI64 address, ordered from destination to source. 46 | """ 47 | 48 | source_route = [] 49 | with self.dataLock: 50 | try: 51 | parents = self._dispatch_and_get_result(signal='getParents', data=None) 52 | self._get_source_route_internal(dest_addr, source_route, parents) 53 | except Exception as err: 54 | log.error(err) 55 | raise 56 | 57 | return source_route 58 | 59 | # ======================== private ========================================= 60 | 61 | def _get_source_route_internal(self, dest_addr, source_route, parents): 62 | 63 | if not dest_addr: 64 | # no more parents 65 | return 66 | 67 | if not parents.get(tuple(dest_addr)): 68 | # this node does not have a list of parents 69 | return 70 | 71 | # first time add destination address 72 | if dest_addr not in source_route: 73 | source_route += [dest_addr] 74 | 75 | # pick a parent 76 | parent = parents.get(tuple(dest_addr))[0] 77 | 78 | # avoid loops 79 | if parent not in source_route: 80 | source_route += [parent] 81 | 82 | # add non empty parents recursively 83 | nextparent = self._get_source_route_internal(parent, source_route, parents) 84 | 85 | if nextparent: 86 | source_route += [nextparent] 87 | 88 | # ======================== helpers ========================================= 89 | -------------------------------------------------------------------------------- /openvisualizer/rpl/topology.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2013, Regents of the University of California. 2 | # All rights reserved. 3 | # 4 | # Released under the BSD 3-Clause license as published at the link below. 5 | # https://openwsn.atlassian.net/wiki/display/OW/License 6 | 7 | """ 8 | Module which receives DAO messages and calculates source routes. 9 | 10 | .. moduleauthor:: Xavi Vilajosana 11 | January 2013 12 | .. moduleauthor:: Thomas Watteyne 13 | April 2013 14 | """ 15 | 16 | import logging 17 | import threading 18 | import time 19 | 20 | from openvisualizer.eventbus.eventbusclient import EventBusClient 21 | 22 | log = logging.getLogger('Topology') 23 | log.setLevel(logging.ERROR) 24 | log.addHandler(logging.NullHandler()) 25 | 26 | 27 | class Topology(EventBusClient): 28 | 29 | def __init__(self): 30 | 31 | # log 32 | log.debug('create instance') 33 | 34 | # local variables 35 | self.data_lock = threading.Lock() 36 | self.parents = {} 37 | self.parents_last_seen = {} 38 | self.NODE_TIMEOUT_THRESHOLD = 900 39 | 40 | super(Topology, self).__init__( 41 | name='topology', 42 | registrations=[ 43 | { 44 | 'sender': self.WILDCARD, 45 | 'signal': 'updateParents', 46 | 'callback': self.update_parents, 47 | }, 48 | { 49 | 'sender': self.WILDCARD, 50 | 'signal': 'getParents', 51 | 'callback': self.get_parents, 52 | }, 53 | ], 54 | ) 55 | 56 | # ======================== public ========================================== 57 | 58 | def get_parents(self, sender, signal, data): 59 | return self.parents 60 | 61 | def get_dag(self): 62 | states = [] 63 | edges = [] 64 | motes = [] 65 | 66 | with self.data_lock: 67 | for src, dsts in self.parents.items(): 68 | src_s = ''.join(['%02X' % x for x in src[-2:]]) 69 | motes.append(src_s) 70 | for dst in dsts: 71 | dst_s = ''.join(['%02X' % x for x in dst[-2:]]) 72 | edges.append({'u': src_s, 'v': dst_s}) 73 | motes.append(dst_s) 74 | motes = list(set(motes)) 75 | for mote in motes: 76 | d = {'id': mote, 'value': {'label': mote}} 77 | states.append(d) 78 | 79 | return states, edges 80 | 81 | def update_parents(self, sender, signal, data): 82 | """ inserts parent information into the parents dictionary """ 83 | with self.data_lock: 84 | # data[0] == source address, data[1] == list of parents 85 | self.parents.update({data[0]: data[1]}) 86 | self.parents_last_seen.update({data[0]: time.time()}) 87 | 88 | self._clear_node_timeout() 89 | 90 | def _clear_node_timeout(self): 91 | threshold = time.time() - self.NODE_TIMEOUT_THRESHOLD 92 | with self.data_lock: 93 | for node in self.parents_last_seen.keys(): 94 | if self.parents_last_seen[node] < threshold: 95 | if node in self.parents: 96 | del self.parents[node] 97 | del self.parents_last_seen[node] 98 | 99 | # ======================== private ========================================= 100 | 101 | # ======================== helpers ========================================= 102 | -------------------------------------------------------------------------------- /openvisualizer/simengine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/openvisualizer/simengine/__init__.py -------------------------------------------------------------------------------- /openvisualizer/simengine/idmanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2010-2013, Regents of the University of California. 3 | # All rights reserved. 4 | # 5 | # Released under the BSD 3-Clause license as published at the link below. 6 | # https://openwsn.atlassian.net/wiki/display/OW/License 7 | 8 | import logging 9 | 10 | 11 | class IdManager(object): 12 | """ The module which assigns ID to the motes. """ 13 | 14 | def __init__(self): 15 | # store params 16 | from openvisualizer.simengine import simengine 17 | self.engine = simengine.SimEngine() 18 | 19 | # local variables 20 | self.current_id = 0 21 | 22 | # logging 23 | self.log = logging.getLogger('IdManager') 24 | self.log.setLevel(logging.INFO) 25 | self.log.addHandler(logging.NullHandler()) 26 | 27 | # ======================== public ========================================== 28 | 29 | def get_id(self): 30 | # increment the running ID 31 | self.current_id += 1 32 | 33 | # debug 34 | if self.log.isEnabledFor(logging.DEBUG): 35 | self.log.debug('assigning ID=' + str(self.current_id)) 36 | 37 | return self.current_id 38 | 39 | # ======================== private ========================================= 40 | 41 | # ======================== helpers ========================================= 42 | -------------------------------------------------------------------------------- /openvisualizer/simengine/locationmanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2010-2013, Regents of the University of California. 3 | # All rights reserved. 4 | # 5 | # Released under the BSD 3-Clause license as published at the link below. 6 | # https://openwsn.atlassian.net/wiki/display/OW/License 7 | 8 | import logging 9 | import random 10 | 11 | 12 | class LocationManager(object): 13 | """ The module which assigns locations to the motes. """ 14 | 15 | def __init__(self): 16 | # store params 17 | from openvisualizer.simengine import simengine 18 | self.engine = simengine.SimEngine() 19 | 20 | # local variables 21 | 22 | # logging 23 | self.log = logging.getLogger('LocationManager') 24 | self.log.setLevel(logging.DEBUG) 25 | self.log.addHandler(logging.NullHandler()) 26 | 27 | # ======================== public ========================================== 28 | 29 | def get_location(self): 30 | # get random location around Cory Hall, UC Berkeley 31 | lat = 37.875095 - 0.0005 + random.random() * 0.0010 32 | lon = -122.257473 - 0.0005 + random.random() * 0.0010 33 | 34 | # debug 35 | if self.log.isEnabledFor(logging.DEBUG): 36 | self.log.debug('assigning location ({0} {1})'.format(lat, lon)) 37 | 38 | return lat, lon 39 | 40 | # ======================== private ========================================= 41 | 42 | # ======================== helpers ========================================= 43 | -------------------------------------------------------------------------------- /openvisualizer/simengine/simengine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2010-2013, Regents of the University of California. 3 | # All rights reserved. 4 | # 5 | # Released under the BSD 3-Clause license as published at the link below. 6 | # https://openwsn.atlassian.net/wiki/display/OW/License 7 | 8 | import logging 9 | import threading 10 | import time 11 | 12 | from openvisualizer.simengine import timeline, propagation, idmanager, locationmanager 13 | 14 | 15 | class SimEngineStats(object): 16 | def __init__(self): 17 | self.durationRunning = 0 18 | self.running = False 19 | self.txStart = None 20 | 21 | def indicate_start(self): 22 | self.txStart = time.time() 23 | self.running = True 24 | 25 | def indicate_stop(self): 26 | if self.txStart: 27 | self.durationRunning += time.time() - self.txStart 28 | self.running = False 29 | 30 | def get_duration_running(self): 31 | if self.running: 32 | return self.durationRunning + (time.time() - self.txStart) 33 | else: 34 | return self.durationRunning 35 | 36 | 37 | class SimEngine(object): 38 | """ The main simulation engine. """ 39 | 40 | # ======================== singleton pattern =============================== 41 | 42 | _instance = None 43 | _init = False 44 | 45 | def __new__(cls, *args, **kwargs): 46 | if not cls._instance: 47 | cls._instance = super(SimEngine, cls).__new__(cls, *args, **kwargs) 48 | return cls._instance 49 | 50 | # ======================== main ============================================ 51 | 52 | def __init__(self, sim_topology='', log_handler=logging.StreamHandler(), log_level=logging.WARNING): 53 | 54 | # don't re-initialize an instance (singleton pattern) 55 | if self._init: 56 | return 57 | self._init = True 58 | 59 | # store params 60 | self.log_handler = log_handler 61 | self.log_handler.setFormatter( 62 | logging.Formatter(fmt='%(asctime)s [%(name)s:%(levelname)s] %(message)s', datefmt='%H:%M:%S')) 63 | 64 | # local variables 65 | self.moteHandlers = [] 66 | self.timeline = timeline.TimeLine() 67 | self.propagation = propagation.Propagation(sim_topology) 68 | self.id_manager = idmanager.IdManager() 69 | self.location_manager = locationmanager.LocationManager() 70 | self.pauseSem = threading.Lock() 71 | self.isPaused = False 72 | self.stopAfterSteps = None 73 | self.delay = 0 74 | self.stats = SimEngineStats() 75 | 76 | # logging this module 77 | self.log = logging.getLogger('SimEngine') 78 | self.log.setLevel(logging.INFO) 79 | self.log.addHandler(logging.NullHandler()) 80 | 81 | # logging core modules 82 | for logger_name in ['SimEngine', 'Timeline', 'Propagation', 'IdManager', 'LocationManager']: 83 | temp = logging.getLogger(logger_name) 84 | temp.setLevel(log_level) 85 | temp.addHandler(log_handler) 86 | 87 | def start(self): 88 | 89 | # log 90 | self.log.info('starting') 91 | 92 | # start timeline 93 | self.timeline.start() 94 | 95 | # ======================== public ========================================== 96 | 97 | # === controlling execution speed 98 | 99 | def set_delay(self, delay): 100 | self.delay = delay 101 | 102 | def pause(self): 103 | if self.log.isEnabledFor(logging.DEBUG): 104 | self.log.debug('pause') 105 | if not self.isPaused: 106 | self.pauseSem.acquire() 107 | self.isPaused = True 108 | self.stats.indicate_stop() 109 | 110 | def step(self, num_steps): 111 | self.stopAfterSteps = num_steps 112 | if self.isPaused: 113 | self.pauseSem.release() 114 | self.isPaused = False 115 | 116 | def resume(self): 117 | if self.log.isEnabledFor(logging.DEBUG): 118 | self.log.debug('resume') 119 | self.stopAfterSteps = None 120 | if self.isPaused: 121 | self.pauseSem.release() 122 | self.isPaused = False 123 | self.stats.indicate_start() 124 | 125 | def pause_or_delay(self): 126 | if self.isPaused: 127 | if self.log.isEnabledFor(logging.DEBUG): 128 | self.log.debug('pauseOrDelay: pause') 129 | self.pauseSem.acquire() 130 | self.pauseSem.release() 131 | else: 132 | if self.log.isEnabledFor(logging.DEBUG): 133 | self.log.debug('pauseOrDelay: delay {0}'.format(self.delay)) 134 | time.sleep(self.delay) 135 | 136 | if self.stopAfterSteps is not None: 137 | if self.stopAfterSteps > 0: 138 | self.stopAfterSteps -= 1 139 | if self.stopAfterSteps == 0: 140 | self.pause() 141 | 142 | assert (self.stopAfterSteps is None or self.stopAfterSteps >= 0) 143 | 144 | def is_running(self): 145 | return not self.isPaused 146 | 147 | # === called from the main script 148 | 149 | def indicate_new_mote(self, new_mote_handler): 150 | 151 | # add this mote to my list of motes 152 | self.moteHandlers.append(new_mote_handler) 153 | 154 | # create connections to already existing motes 155 | for mh in self.moteHandlers[:-1]: 156 | self.propagation.create_connection( 157 | from_mote=new_mote_handler.get_id(), 158 | to_mote=mh.get_id(), 159 | ) 160 | 161 | # === called from timeline 162 | 163 | def indicate_first_event_passed(self): 164 | self.stats.indicate_start() 165 | 166 | # === getting information about the system 167 | 168 | def get_num_motes(self): 169 | return len(self.moteHandlers) 170 | 171 | def get_mote_handler(self, rank): 172 | return self.moteHandlers[rank] 173 | 174 | def get_mote_handler_by_id(self, mote_id): 175 | return_val = None 176 | for h in self.moteHandlers: 177 | if h.get_id() == mote_id: 178 | return_val = h 179 | break 180 | assert return_val 181 | return return_val 182 | 183 | def get_stats(self): 184 | return self.stats 185 | 186 | # ======================== private ========================================= 187 | 188 | # ======================== helpers ========================================= 189 | -------------------------------------------------------------------------------- /openvisualizer/topologies/0001-mesh.json: -------------------------------------------------------------------------------- 1 | {"connections": [{"fromMote": 1, "toMote": 2, "pdr": 1.0}, {"fromMote": 1, "toMote": 4, "pdr": 1.0}, {"fromMote": 2, "toMote": 4, "pdr": 1.0}, {"fromMote": 2, "toMote": 6, "pdr": 1.0}, {"fromMote": 3, "toMote": 4, "pdr": 1.0}, {"fromMote": 3, "toMote": 5, "pdr": 1.0}, {"fromMote": 4, "toMote": 5, "pdr": 1.0}, {"fromMote": 4, "toMote": 6, "pdr": 1.0}, {"fromMote": 5, "toMote": 6, "pdr": 1.0}], "motes": [{"lat": 37.87555371618762, "lon": -122.25802509927024, "id": 1}, {"lat": 37.875147290614436, "lon": -122.25823515906504, "id": 2}, {"lat": 37.87518315488792, "lon": -122.2566987075463, "id": 3}, {"lat": 37.87524531846254, "lon": -122.25757342064209, "id": 4}, {"lat": 37.874809787643855, "lon": -122.25710741906954, "id": 5}, {"lat": 37.87473092334277, "lon": -122.25787189707734, "id": 6}], "DAGroot": null} -------------------------------------------------------------------------------- /openvisualizer/topologies/0002-star.json: -------------------------------------------------------------------------------- 1 | {"connections": [{"fromMote": 1, "toMote": 2, "pdr": 1.0}, {"fromMote": 1, "toMote": 3, "pdr": 1.0}, {"fromMote": 1, "toMote": 4, "pdr": 1.0}, {"fromMote": 1, "toMote": 5, "pdr": 1.0}, {"fromMote": 1, "toMote": 6, "pdr": 1.0}], "motes": [{"lat": 37.87509598650428, "lon": -122.25759092425096, "id": 1}, {"lat": 37.874729939052514, "lon": -122.25784057624395, "id": 2}, {"lat": 37.875517807129, "lon": -122.25738268306627, "id": 3}, {"lat": 37.8753043278405, "lon": -122.25807861561823, "id": 4}, {"lat": 37.87519625791454, "lon": -122.25692360086381, "id": 5}, {"lat": 37.874745922468485, "lon": -122.25717153357121, "id": 6}], "DAGroot": null} -------------------------------------------------------------------------------- /openvisualizer/topologies/README.md: -------------------------------------------------------------------------------- 1 | This directory contains several example network topologies saved in JSON format. 2 | The topologies can be loaded by OpenVisualizer with the command: 3 | 4 | `openv-server --load-topology=` 5 | 6 | To inspect the topology you can use the web interface: 7 | 8 | `openv-server --load-topology= --webserver=` 9 | 10 | Under the tab _topology_ you can see and manipulate the network topology. 11 | 12 | ![openvisualizer_web_interface](../../images/web_interface_topology.png "A star topology") 13 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyserial 2 | PyDispatcher 3 | bottle 4 | Sphinx 5 | intelhex 6 | setuptools<=44.1.1 7 | openwsn-coap>=0.0.7 8 | pycryptodome 9 | cbor 10 | hkdf 11 | paho-mqtt 12 | coloredlogs 13 | verboselogs 14 | click 15 | blessed 16 | ipaddr 17 | scapy 18 | sshtunnel 19 | iotlabcli 20 | appdirs 21 | pywin32; sys_platform == 'win32' 22 | colorama; sys_platform == 'win32' 23 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/scripts/__init__.py -------------------------------------------------------------------------------- /scripts/serialtester_cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import logging 4 | import time 5 | 6 | import click 7 | import coloredlogs 8 | 9 | from openvisualizer.motehandler.moteprobe import serialmoteprobe 10 | from openvisualizer.motehandler.moteprobe.serialmoteprobe import SerialMoteProbe 11 | from openvisualizer.motehandler.moteprobe.serialtester import SerialTester 12 | 13 | for logger in [logging.getLogger(__name__), serialmoteprobe.log]: 14 | coloredlogs.install(logger=logger, fmt="%(asctime)s [%(name)s:%(levelname)s] %(message)s", datefmt="%H:%m:%S", 15 | level='WARNING') 16 | 17 | 18 | def serialtest_tracer(msg): 19 | if '---' in msg: 20 | click.secho('\n' + msg, fg='blue', bold=True) 21 | elif 'received' in msg: 22 | click.secho(msg, fg='green') 23 | else: 24 | click.secho(msg) 25 | 26 | 27 | @click.command() 28 | @click.argument('port', required=True, nargs=1, type=str) 29 | @click.option('--baudrate', default=115200, show_default=True, help='Specify baudrate') 30 | @click.option('--verbose', is_flag=True, help='Enable debug output from serialtester') 31 | @click.option('-r', '--runs', default=100, show_default=True, help='Test iterations') 32 | @click.option('-l', '--pktlen', default=100, show_default=True, help='Length of the echo packet') 33 | @click.option('-t', '--timeout', default=2, show_default=True, help='Timeout on echo reception (in seconds)') 34 | def cli(port, baudrate, verbose, runs, pktlen, timeout): 35 | """ Serial Tester tool """ 36 | 37 | click.secho("Serial Tester Script...", bold=True) 38 | 39 | smp = SerialMoteProbe(port=port, baudrate=baudrate) 40 | 41 | while smp.serial is None: 42 | time.sleep(0.1) 43 | 44 | logger.info("initialized serial object") 45 | 46 | tester = SerialTester(smp) 47 | 48 | if verbose: 49 | tester.set_trace(serialtest_tracer) 50 | 51 | tester.set_num_test_pkt(runs) 52 | tester.set_test_pkt_length(pktlen) 53 | tester.set_timeout(timeout) 54 | 55 | click.secho("\nTest Setup:", bold=True) 56 | click.secho("----------------") 57 | click.secho("Iterations: {:>6}".format(runs)) 58 | click.secho("Packet length: {:>3}".format(pktlen)) 59 | click.secho("Echo timeout: {:>4}".format(timeout)) 60 | 61 | click.secho("\nTest Progress:\n") 62 | # start test 63 | if verbose: 64 | tester.test(blocking=True) 65 | else: 66 | tester.test(blocking=False) 67 | 68 | with click.progressbar(range(runs)) as bar: 69 | for x in bar: 70 | while tester.stats['numOk'] < x: 71 | time.sleep(0.2) 72 | 73 | time.sleep(0.5) 74 | res = tester.get_stats() 75 | click.secho("\n\nTest Statistics:", bold=True) 76 | click.secho("----------------") 77 | click.secho("Pkts send: {:>8}".format(res['numSent'])) 78 | click.secho("Echo success: {:>5}".format(res['numOk']), fg='green') 79 | click.secho("Echo timeout: {:>5}".format(res['numTimeout']), fg='yellow') 80 | click.secho("Echo corrupted: {:>3}".format(res['numCorrupted']), fg='red') 81 | 82 | click.secho("\nKill with Ctrl-C.\n") 83 | 84 | while True: 85 | try: 86 | time.sleep(0.5) 87 | except KeyboardInterrupt: 88 | smp.close() 89 | smp.join() 90 | break 91 | 92 | logger.info("quitting script") 93 | 94 | 95 | if __name__ == "__main__": 96 | cli() 97 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup, find_packages 4 | 5 | from openvisualizer import VERSION, PACKAGE_NAME 6 | 7 | web_static = 'client/web_files/static' 8 | web_templates = 'client/web_files/templates' 9 | 10 | 11 | # Cannot create this list with pip.req.parse_requirements() because it requires 12 | # the pwd module, which is Unix only. 13 | def _read_requirements(file_name): 14 | """ 15 | Returns list of required modules for 'install_requires' parameter. Assumes 16 | requirements file contains only module lines and comments. 17 | """ 18 | requirements = [] 19 | with open(os.path.join(file_name)) as f: 20 | for line in f: 21 | if not line.startswith('#'): 22 | requirements.append(line) 23 | return requirements 24 | 25 | 26 | INSTALL_REQUIREMENTS = _read_requirements('requirements.txt') 27 | 28 | # read the contents of your README file 29 | this_directory = os.path.abspath(os.path.dirname(__file__)) 30 | with open(os.path.join(this_directory, 'README.md')) as f: 31 | LONG_DESCRIPTION = f.read() 32 | 33 | setup( 34 | name=PACKAGE_NAME, 35 | packages=find_packages(exclude=['tests', '*.tests', 'tests.*', '*.tests.*']), 36 | python_requires='<=2.7.18', 37 | include_package_data=True, 38 | entry_points={ 39 | 'console_scripts': [ 40 | 'openv-server = openvisualizer.__main__:main', 41 | 'openv-client = openvisualizer.client.main:cli', 42 | 'openv-serial = scripts.serialtester_cli:cli', 43 | 'openv-tun = scripts.ping_responder:cli', 44 | ], 45 | }, 46 | install_requires=INSTALL_REQUIREMENTS, 47 | # Must extract zip to edit conf files. 48 | zip_safe=False, 49 | version=VERSION, 50 | author='Thomas Watteyne', 51 | author_email='watteyne@eecs.berkeley.edu', 52 | description='Wireless sensor network monitoring and visualization tool', 53 | long_description_content_type='text/markdown', 54 | long_description=LONG_DESCRIPTION, 55 | url='https://openwsn.atlassian.net/wiki/display/OW/OpenVisualizer', 56 | keywords=['6TiSCH', 'Internet of Things', '6LoWPAN', '802.15.4e', 'sensor', 'mote'], 57 | platforms=['platform-independent'], 58 | license='BSD 3-Clause', 59 | classifiers=[ 60 | 'Development Status :: 3 - Alpha', 61 | 'Intended Audience :: Developers', 62 | 'License :: OSI Approved :: BSD License', 63 | 'Operating System :: OS Independent', 64 | 'Programming Language :: Python', 65 | 'Programming Language :: Python :: 2.7', 66 | 'Topic :: Communications', 67 | 'Topic :: Home Automation', 68 | 'Topic :: Internet', 69 | 'Topic :: Software Development', 70 | ], 71 | ) 72 | -------------------------------------------------------------------------------- /tests-requirements.txt: -------------------------------------------------------------------------------- 1 | mock 2 | pytest==4.6.9 3 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import errno 2 | import json 3 | import logging 4 | import os 5 | import select 6 | import socket 7 | import struct 8 | import time 9 | import xmlrpclib 10 | from fcntl import ioctl 11 | from subprocess import Popen 12 | 13 | import pytest 14 | from ipaddr import IPv6Address 15 | from scapy.layers.inet6 import IPv6 16 | 17 | from openvisualizer.client.utils import transform_into_ipv6 18 | from openvisualizer.motehandler.motestate.motestate import MoteState 19 | 20 | log = logging.getLogger(__name__) 21 | 22 | # ============================ helpers & setup code ============================= 23 | 24 | HOST = "localhost" 25 | PORT = 9000 26 | 27 | ADDRESSES = [] 28 | 29 | # Connect to a running instance of openv-server and retrieve the addresses of the motes in the network 30 | url = 'http://{}:{}'.format(HOST, str(PORT)) 31 | try: 32 | rpc_server = xmlrpclib.ServerProxy(url) 33 | mote_ids = rpc_server.get_mote_dict().keys() 34 | 35 | if None not in mote_ids: 36 | for addr in mote_ids: 37 | mote_state = rpc_server.get_mote_state(addr) 38 | id_manager = json.loads(mote_state[MoteState.ST_IDMANAGER])[0] 39 | ipv6_addr = transform_into_ipv6(id_manager['myPrefix'][:-9] + '-' + id_manager['my64bID'][:-5]) 40 | if ipv6_addr[:4] == 'fe80': 41 | pytest.exit("Is the network running? Mote has link local address'") 42 | else: 43 | ADDRESSES.append(ipv6_addr) 44 | else: 45 | pytest.exit("Is the network running? Mote address resolve to 'None'") 46 | 47 | # remove dagroot from address list, since it only relays packet 48 | dag_root = rpc_server.get_dagroot() 49 | if dag_root is None: 50 | pytest.exit("There is no DAG root configured in the network") 51 | 52 | dag_root = "".join('%02x' % b for b in dag_root) 53 | 54 | for addr in ADDRESSES: 55 | if addr[-4:] == dag_root: 56 | ADDRESSES.remove(addr) 57 | 58 | except socket.error as err: 59 | if errno.ECONNREFUSED: 60 | log.warning( 61 | "If you are trying to run a firmware test you need a running instance of openv-server with the option " 62 | "'--opentun'") 63 | else: 64 | log.error(err) 65 | except xmlrpclib as err: 66 | log.error("Caught server fault -- {}".format(err)) 67 | 68 | 69 | def is_my_icmpv6(ipv6_pkt, his_address, my_address, next_header): 70 | if IPv6Address(ipv6_pkt.src).exploded == IPv6Address(his_address).exploded and \ 71 | IPv6Address(ipv6_pkt.dst).exploded == IPv6Address(my_address).exploded and \ 72 | ipv6_pkt.nh == next_header: 73 | return True 74 | else: 75 | return False 76 | 77 | 78 | class TunInterface: 79 | VIRTUAL_TUN_ID = [0x00, 0x00, 0x86, 0xdd] 80 | 81 | IFF_TUN = 0x0001 82 | TUN_SET_IFF = 0x400454ca 83 | ETHERNET_MTU = 1500 84 | 85 | def __init__(self, prefix='cccc::', host='1'): 86 | self.ipv6_prefix = prefix 87 | self.ipv6_host = host 88 | 89 | try: 90 | self.tun_iff = self._create_tun_if() 91 | except IOError: 92 | pytest.exit("Opening a TUN interface requires sudo privileges") 93 | 94 | def _create_tun_if(self): 95 | return_val = os.open("/dev/net/tun", os.O_RDWR) 96 | ifs = ioctl(return_val, self.TUN_SET_IFF, struct.pack("16sH", "tun%d", self.IFF_TUN)) 97 | self.if_name = ifs.decode('UTF-8')[:16].strip("\x00") 98 | 99 | os.system('ip tuntap add dev ' + self.if_name + ' mode tun user root') 100 | os.system('ip link set ' + self.if_name + ' up') 101 | os.system('ip -6 addr add ' + self.ipv6_prefix + self.ipv6_host + '/64 dev ' + self.if_name) 102 | os.system('ip -6 addr add fe80::' + self.ipv6_host + '/64 dev ' + self.if_name) 103 | 104 | return return_val 105 | 106 | def read(self, dest=None, count=1, timeout=0): 107 | received = [] 108 | while True: 109 | readable, _, _ = select.select([self.tun_iff], [], [], timeout) 110 | if len(readable) > 0: 111 | pkt_byte = os.read(self.tun_iff, self.ETHERNET_MTU)[4:] 112 | 113 | if IPv6(pkt_byte).version == 6: 114 | if dest is not None: 115 | if IPv6Address(IPv6(pkt_byte).dst).exploded == IPv6Address(dest).exploded: 116 | received.append(pkt_byte) 117 | else: 118 | received.append(pkt_byte) 119 | 120 | if 1 <= count == len(received): 121 | break 122 | else: 123 | break 124 | 125 | return received 126 | 127 | def write(self, data): 128 | if not self.tun_iff: 129 | return 130 | 131 | # add tun header and convert to bytes 132 | data = self.VIRTUAL_TUN_ID + data 133 | data = "".join([chr(b) for b in data]) 134 | 135 | try: 136 | # write over tuntap interface 137 | os.write(self.tun_iff, data) 138 | except OSError as e: 139 | log.error(e) 140 | 141 | 142 | # ============================= fixtures ====================================== 143 | 144 | @pytest.fixture(params=ADDRESSES) 145 | def mote_addr(request): 146 | return request.param 147 | 148 | 149 | @pytest.fixture(scope="session") 150 | def etun(): 151 | return TunInterface() 152 | 153 | 154 | @pytest.fixture() 155 | def server(): 156 | arguments = ['openv-server', '--sim=2', '--no-boot'] 157 | server_proc = Popen(arguments, shell=False) 158 | 159 | # give openv-server time to boot 160 | time.sleep(3) 161 | 162 | yield server_proc 163 | 164 | # kill the server 165 | server_proc.terminate() 166 | 167 | 168 | @pytest.fixture() 169 | def server_booted(): 170 | arguments = ['openv-server', '--sim=2'] 171 | server_proc = Popen(arguments, shell=False) 172 | 173 | # give openv-server time to boot 174 | time.sleep(3) 175 | 176 | yield server_proc 177 | 178 | # kill the server 179 | server_proc.terminate() 180 | -------------------------------------------------------------------------------- /tests/fw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/tests/fw/__init__.py -------------------------------------------------------------------------------- /tests/fw/test_fw_frag.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import pytest 4 | from Crypto.Random.random import randint 5 | from scapy.compat import raw 6 | from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply 7 | 8 | from tests.conftest import is_my_icmpv6 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | # =========================== defines ========================================== 13 | 14 | NH_ICMPV6 = 58 15 | 16 | LOCAL_ADDR = "cccc::2" 17 | 18 | 19 | # ============================ tests =========================================== 20 | 21 | 22 | @pytest.mark.parametrize("payload_len", range(100, 500, 100)) 23 | def test_basic_6lo_fragmentation(etun, mote_addr, payload_len): 24 | """ Test basic 6LoWPAN fragmentation and reassembly functions """ 25 | 26 | ip = IPv6(src=LOCAL_ADDR, dst=mote_addr, hlim=64) 27 | id = randint(0, 65535) 28 | seq = randint(0, 65535) 29 | icmp = ICMPv6EchoRequest(id=id, seq=seq) 30 | pkt = ip / icmp 31 | 32 | payload = "".join([chr(randint(0, 255))] * payload_len) 33 | pkt.add_payload(payload) 34 | 35 | etun.write(list(bytearray(raw(pkt)))) 36 | received = etun.read(dest=LOCAL_ADDR, timeout=15) 37 | 38 | timeout = True 39 | for recv_pkt in received: 40 | ipv6_pkt = IPv6(recv_pkt) 41 | if is_my_icmpv6(ipv6_pkt, mote_addr, LOCAL_ADDR, NH_ICMPV6): 42 | timeout = False 43 | icmp = ICMPv6EchoReply(raw(ipv6_pkt)[40:]) 44 | # check if icmp headers match 45 | assert icmp.id == id 46 | assert icmp.seq == seq 47 | 48 | if timeout: 49 | # node to failed to respond with an ICMPv6 echo before timeout 50 | pytest.fail("Timeout on ICMPv6 Echo Response!") 51 | 52 | 53 | # @pytest.mark.sim_only 54 | def test_cleanup_on_fragment_loss(etun): 55 | """ 56 | Test the cleanup function after a fragment loss 57 | """ 58 | 59 | pass 60 | -------------------------------------------------------------------------------- /tests/fw/test_fw_udp.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from random import randint, choice 3 | 4 | import pytest 5 | from scapy.compat import raw 6 | from scapy.layers.inet import UDP 7 | from scapy.layers.inet6 import IPv6 8 | 9 | from tests.conftest import is_my_icmpv6 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | # =========================== defines ========================================== 14 | LOCAL_ADDR = 'cccc::2' 15 | UDP_ECHO_PORT = 7 16 | 17 | NH_UDP = 17 18 | 19 | 20 | # ============================ tests =========================================== 21 | 22 | @pytest.mark.parametrize("payload", 23 | ["hello", "hello from", "hello from the", "hello from the other", "hello from the other side"]) 24 | def test_udp_echo(etun, mote_addr, payload): 25 | local_sport = randint(1024, 65355) 26 | 27 | ip = IPv6(src=LOCAL_ADDR, dst=mote_addr, hlim=64) 28 | udp = UDP(sport=local_sport, dport=UDP_ECHO_PORT) 29 | pkt = ip / udp 30 | 31 | pkt.add_payload(payload) 32 | 33 | _verify_test_answer(etun, pkt, mote_addr, local_sport) 34 | 35 | 36 | @pytest.mark.xfail(reason='Invalid checksum') 37 | def test_udp_checksum(etun, mote_addr): 38 | local_sport = randint(1024, 65355) 39 | 40 | ip = IPv6(src=LOCAL_ADDR, dst=mote_addr, hlim=64) 41 | udp = UDP(sport=local_sport, dport=UDP_ECHO_PORT) 42 | pkt = ip / udp 43 | 44 | payload = "test packet with wrong checksum" 45 | pkt.add_payload(payload) 46 | logger.info("Resetting checksum") 47 | pkt[UDP].chksum = 0 48 | 49 | _verify_test_answer(etun, pkt, mote_addr, local_sport) 50 | 51 | 52 | @pytest.mark.parametrize("payload_len", range(0, 25)) 53 | def test_multiple_connection_echo(etun, mote_addr, payload_len): 54 | local_sport = randint(1024, 65355) 55 | 56 | address_list = ['CCCC::0002', 'CCCC::45ab:0009', 'CCCC::deaf', 'CCCC::beef'] 57 | my_address = choice(address_list) 58 | 59 | ip = IPv6(src=my_address, dst=mote_addr, hlim=64) 60 | udp = UDP(sport=local_sport, dport=UDP_ECHO_PORT) 61 | pkt = ip / udp 62 | 63 | pkt.add_payload("".join([chr(randint(0, 255))] * payload_len)) 64 | 65 | _verify_test_answer(etun, pkt, mote_addr, local_sport, my_address) 66 | 67 | 68 | def _verify_test_answer(etun, pkt, mote_addr, local_sport, my_address=None): 69 | etun.write(list(bytearray(raw(pkt)))) 70 | 71 | if my_address is None: 72 | addr = LOCAL_ADDR 73 | else: 74 | addr = my_address 75 | 76 | received = etun.read(dest=addr, timeout=15) 77 | 78 | timeout = True 79 | for recv_pkt in received: 80 | ipv6_pkt = IPv6(recv_pkt) 81 | if is_my_icmpv6(ipv6_pkt, mote_addr, addr, NH_UDP): 82 | timeout = False 83 | udp = UDP(raw(ipv6_pkt)[40:]) 84 | assert udp.sport == UDP_ECHO_PORT 85 | assert udp.dport == local_sport 86 | 87 | if timeout: 88 | # node to failed to respond with an ICMPv6 echo before timeout 89 | pytest.fail("Timeout on UDP Echo!") 90 | -------------------------------------------------------------------------------- /tests/ov/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/tests/ov/__init__.py -------------------------------------------------------------------------------- /tests/ov/test_frag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import logging.handlers 4 | from random import randint, shuffle 5 | 6 | import pytest 7 | import scapy.layers.inet6 as ip6 8 | import scapy.layers.sixlowpan as lo 9 | from scapy.compat import raw 10 | 11 | from openvisualizer.openlbr import sixlowpan_frag 12 | 13 | # ============================ logging ========================================= 14 | LOGFILE_NAME = 'test_frag.log' 15 | 16 | log = logging.getLogger('test_frag') 17 | log.setLevel(logging.ERROR) 18 | log.addHandler(logging.NullHandler()) 19 | 20 | log_handler = logging.handlers.RotatingFileHandler(LOGFILE_NAME, backupCount=5, mode='w') 21 | log_handler.setFormatter(logging.Formatter("%(asctime)s [%(name)s:%(levelname)s] %(message)s")) 22 | for logger_name in ['test_frag', 'SixLowPanFrag']: 23 | temp = logging.getLogger(logger_name) 24 | temp.setLevel(logging.DEBUG) 25 | temp.addHandler(log_handler) 26 | 27 | # ============================ defines ========================================= 28 | 29 | NUM_OF_TEST_VECTORS = 100 30 | MAX_PAYLOAD_SIZE = 1280 31 | MIN_PAYLOAD_SIZE = 0 32 | 33 | # ============================ fixtures ======================================== 34 | TEST_VECTORS = [] 35 | 36 | for i in range(NUM_OF_TEST_VECTORS): 37 | # create an IPv6 packet with a random payload 38 | pkt = ip6.IPv6(src='bbbb::2', dst='bbbb::1', hlim=64) 39 | pkt.add_payload("".join([chr(randint(0, 255)) for j in range(randint(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE))])) 40 | 41 | # fragment the packet 42 | fragment_list = lo.sixlowpan_fragment(pkt) 43 | 44 | for j in range(len(fragment_list)): 45 | fragment_list[j] = [ord(b) for b in raw(fragment_list[j])] 46 | 47 | pkt = [ord(b) for b in raw(pkt)] 48 | 49 | TEST_VECTORS.append((pkt, fragment_list)) 50 | 51 | 52 | @pytest.fixture(params=TEST_VECTORS) 53 | def random_6lwp_fragments(request): 54 | return request.param 55 | 56 | 57 | # ============================ tests =========================================== 58 | def test_reassemble_fragments(random_6lwp_fragments): 59 | ip_pkt, frag_list = random_6lwp_fragments 60 | 61 | # fragments can arrive out-of-order 62 | shuffle(frag_list) 63 | 64 | log.debug("test_reassemble_fragments") 65 | 66 | assembler = sixlowpan_frag.Fragmentor() 67 | 68 | for frag in frag_list: 69 | result = assembler.do_reassemble(frag) 70 | 71 | if result is not None: 72 | assert result == ip_pkt 73 | 74 | 75 | def test_fragment_packet(random_6lwp_fragments): 76 | ip_pkt, frag_list = random_6lwp_fragments 77 | 78 | log.debug("test_fragment_packet") 79 | 80 | fragmentor = sixlowpan_frag.Fragmentor() 81 | 82 | log.debug("Original packet (len: {}) -- {}".format(len(ip_pkt), ip_pkt)) 83 | 84 | frags = [lo.SixLoWPAN("".join([chr(b) for b in f])) for f in fragmentor.do_fragment(ip_pkt)] 85 | log.debug(frags) 86 | 87 | reassembled = lo.sixlowpan_defragment(frags) 88 | 89 | if len(reassembled) == 0: 90 | # the packet was not fragmented 91 | log.debug(list(bytearray(raw(frags[0])))) 92 | log.debug(ip_pkt) 93 | assert ip_pkt == list(bytearray(raw(frags[0]))) 94 | else: 95 | log.debug(list(bytearray(raw(reassembled[1])))) 96 | log.debug(ip_pkt) 97 | assert ip_pkt == list(bytearray(raw(reassembled[1]))) 98 | -------------------------------------------------------------------------------- /tests/ov/test_hdlc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import json 4 | import logging 5 | import logging.handlers 6 | import random 7 | 8 | import pytest 9 | 10 | from openvisualizer.motehandler.moteprobe import openhdlc 11 | 12 | # ============================ logging ========================================= 13 | from openvisualizer.utils import format_string_buf 14 | 15 | LOGFILE_NAME = 'test_hdlc.log' 16 | 17 | log = logging.getLogger('test_hdlc') 18 | log.setLevel(logging.ERROR) 19 | log.addHandler(logging.NullHandler()) 20 | 21 | log_handler = logging.handlers.RotatingFileHandler(LOGFILE_NAME, maxBytes=2 * 1024 * 1024, backupCount=5, mode='w') 22 | log_handler.setFormatter(logging.Formatter("%(asctime)s [%(name)s:%(levelname)s] %(message)s")) 23 | for logger_name in ['test_hdlc', 'OpenHdlc']: 24 | temp = logging.getLogger(logger_name) 25 | temp.setLevel(logging.DEBUG) 26 | temp.addHandler(log_handler) 27 | 28 | # ============================ fixtures ======================================== 29 | 30 | RANDOM_FRAME = [] 31 | for frame_len in range(1, 100, 5): 32 | for run in range(100): 33 | frame = None 34 | while (not frame) or (frame in RANDOM_FRAME): 35 | frame = [] 36 | for _ in range(frame_len): 37 | frame += [random.randint(0x00, 0xff)] 38 | RANDOM_FRAME.append(frame) 39 | RANDOM_FRAME = [json.dumps(f) for f in RANDOM_FRAME] 40 | 41 | 42 | @pytest.fixture(params=RANDOM_FRAME) 43 | def random_frame(request): 44 | return request.param 45 | 46 | 47 | # ============================ helpers ========================================= 48 | 49 | # ============================ tests =========================================== 50 | 51 | def test_build_request_frame(): 52 | if log.isEnabledFor(logging.DEBUG): 53 | log.debug("\n---------- test_buildRequestFrame") 54 | 55 | hdlc = openhdlc.OpenHdlc() 56 | 57 | # hdlcify 58 | frame_hdlcified = hdlc.hdlcify('\x53') 59 | log.debug("request frame: {0}".format(format_string_buf(frame_hdlcified))) 60 | 61 | 62 | def test_dehdlcify_to_zero(): 63 | log.debug("\n---------- test_dehdlcify_to_zero") 64 | 65 | hdlc = openhdlc.OpenHdlc() 66 | 67 | # hdlc_frame 68 | hdlc_frame = [0x53, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa] 69 | hdlc_frame = ''.join([chr(b) for b in hdlc_frame]) 70 | log.debug("hdlc_frame: {0}".format(format_string_buf(hdlc_frame))) 71 | 72 | # hdlcify 73 | hdlc_frame = hdlc.hdlcify(hdlc_frame) 74 | log.debug("hdlcify: {0}".format(format_string_buf(hdlc_frame))) 75 | 76 | # remove flags 77 | hdlc_frame = hdlc_frame[1:-1] 78 | log.debug("no flags: {0}".format(format_string_buf(hdlc_frame))) 79 | 80 | # calculate CRC 81 | crcini = 0xffff 82 | crc = crcini 83 | for c in hdlc_frame: 84 | tmp = crc ^ (ord(c)) 85 | crc = (crc >> 8) ^ hdlc.FCS16TAB[(tmp & 0xff)] 86 | log.debug("after {0}, crc={1}".format(hex(ord(c)), hex(crc))) 87 | 88 | 89 | def test_randdom_back_and_forth(random_frame): 90 | random_frame = json.loads(random_frame) 91 | random_frame = ''.join([chr(b) for b in random_frame]) 92 | 93 | log.debug("\n---------- test_randdom_back_and_forth") 94 | 95 | hdlc = openhdlc.OpenHdlc() 96 | 97 | log.debug("random_frame: {0}".format(format_string_buf(random_frame))) 98 | 99 | # hdlcify 100 | frame_hdlcified = hdlc.hdlcify(random_frame) 101 | log.debug("hdlcified: {0}".format(format_string_buf(frame_hdlcified))) 102 | 103 | # dehdlcify 104 | frame_dehdlcified = hdlc.dehdlcify(frame_hdlcified) 105 | log.debug("dehdlcified: {0}".format(format_string_buf(frame_dehdlcified))) 106 | 107 | assert frame_dehdlcified == random_frame 108 | -------------------------------------------------------------------------------- /tests/ov/test_iotlabmoteprobe.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python2 2 | 3 | import logging.handlers 4 | import socket 5 | import time 6 | 7 | import mock 8 | 9 | from openvisualizer.motehandler.moteprobe.iotlabmoteprobe import IotlabMoteProbe 10 | 11 | # ============================ defines ================================= 12 | 13 | MODULE_PATH = 'openvisualizer.motehandler.moteprobe.iotlabmoteprobe' 14 | 15 | 16 | # ============================ fixtures ================================ 17 | 18 | 19 | # ============================ tests =================================== 20 | 21 | def test_iotlabmoteprobe__get_free_port(): 22 | port = IotlabMoteProbe._get_free_port() 23 | # No context manager for sockets in python2.7 24 | 25 | try: 26 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 | s.bind(('', port)) 28 | is_open = True 29 | except socket.error: 30 | pass 31 | else: 32 | assert is_open 33 | finally: 34 | s.close() 35 | 36 | 37 | @mock.patch("{}.IotlabMoteProbe._attach".format(MODULE_PATH)) 38 | @mock.patch("{}.IotlabMoteProbe._detach".format(MODULE_PATH)) 39 | @mock.patch("{}.MoteProbe.__init__".format(MODULE_PATH)) 40 | def test_iotlabmoteprobe___init__(m_parent_init, m_detach, m_attach): 41 | test_node_url = 'm3-10' 42 | mote = IotlabMoteProbe(test_node_url) 43 | assert not hasattr(mote, 'iotlab_site') 44 | 45 | test_node_url = 'm3-10.saclay.iot-lab.info' 46 | mote = IotlabMoteProbe(test_node_url) 47 | assert mote.iotlab_site == 'saclay' 48 | 49 | 50 | def test_iotlabmoteprobe__attach_error_on_frontend(caplog): 51 | # Invalid URL provided while "on" ssh frontend 52 | try: 53 | with caplog.at_level(logging.DEBUG, logger="MoteProbe"): 54 | mote = IotlabMoteProbe('dummy-10') 55 | timeout = 100 56 | while mote.isAlive() and timeout: 57 | time.sleep(0.01) 58 | timeout = timeout - 1 59 | assert mote.isAlive() is False 60 | assert 'Name or service not known' in caplog.text 61 | except Exception as e: 62 | mote.close() 63 | mote.join() 64 | raise e 65 | -------------------------------------------------------------------------------- /tests/ov/test_sourceroute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import json 4 | import logging 5 | import logging.handlers 6 | 7 | import pytest 8 | 9 | from openvisualizer.rpl import topology 10 | from openvisualizer.rpl.sourceroute import SourceRoute 11 | 12 | # ============================ logging ========================================= 13 | from openvisualizer.utils import format_addr 14 | 15 | LOGFILE_NAME = 'test_sourceroute.log' 16 | 17 | log = logging.getLogger('test_sourceRoute') 18 | log.setLevel(logging.ERROR) 19 | log.addHandler(logging.NullHandler()) 20 | 21 | log_handler = logging.handlers.RotatingFileHandler(LOGFILE_NAME, backupCount=5, mode='w') 22 | log_handler.setFormatter(logging.Formatter("%(asctime)s [%(name)s:%(levelname)s] %(message)s")) 23 | for logger_name in ['test_sourceRoute', 'SourceRoute']: 24 | temp = logging.getLogger(logger_name) 25 | temp.setLevel(logging.DEBUG) 26 | temp.addHandler(log_handler) 27 | 28 | # ============================ defines ========================================= 29 | 30 | MOTE_A = [0xaa] * 8 31 | MOTE_B = [0xbb] * 8 32 | MOTE_C = [0xcc] * 8 33 | MOTE_D = [0xdd] * 8 34 | 35 | # ============================ fixtures ======================================== 36 | 37 | EXPECTED_SOURCE_ROUTE = [ 38 | json.dumps((MOTE_B, [MOTE_B, MOTE_A])), 39 | json.dumps((MOTE_C, [MOTE_C, MOTE_B, MOTE_A])), 40 | json.dumps((MOTE_D, [MOTE_D, MOTE_C, MOTE_B, MOTE_A])), 41 | ] 42 | 43 | 44 | @pytest.fixture(params=EXPECTED_SOURCE_ROUTE) 45 | def expected_source_route(request): 46 | return request.param 47 | 48 | 49 | # ============================ helpers ========================================= 50 | 51 | # ============================ tests =========================================== 52 | 53 | def test_source_route(expected_source_route): 54 | """ 55 | This tests the following topology 56 | 57 | MOTE_A <- MOTE_B <- MOTE_C <- MOTE_D 58 | """ 59 | 60 | source_route = SourceRoute() 61 | _ = topology.Topology() 62 | 63 | source_route.dispatch(signal='updateParents', data=(tuple(MOTE_B), [MOTE_A])) 64 | source_route.dispatch(signal='updateParents', data=(tuple(MOTE_C), [MOTE_B])) 65 | source_route.dispatch(signal='updateParents', data=(tuple(MOTE_D), [MOTE_C])) 66 | 67 | expected_destination = json.loads(expected_source_route)[0] 68 | expected_route = json.loads(expected_source_route)[1] 69 | calculated_route = source_route.get_source_route(expected_destination) 70 | 71 | # log 72 | if log.isEnabledFor(logging.DEBUG): 73 | output = [] 74 | output += ['\n'] 75 | output += ['expected_destination: {0}'.format(format_addr(expected_destination))] 76 | output += ['expectedRoute:'] 77 | for m in expected_route: 78 | output += ['- {0}'.format(format_addr(m))] 79 | output += ['calculatedRoute:'] 80 | for m in calculated_route: 81 | output += ['- {0}'.format(format_addr(m))] 82 | output = '\n'.join(output) 83 | log.debug(output) 84 | 85 | assert calculated_route == expected_route 86 | -------------------------------------------------------------------------------- /tests/ov/test_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import logging.handlers 5 | 6 | import pytest 7 | 8 | # ============================ logging ========================================= 9 | from openvisualizer.utils import byteinverse, hex2buf, format_ipv6_addr, buf2int 10 | 11 | LOGFILE_NAME = 'test_utils.log' 12 | 13 | log = logging.getLogger('test_utils') 14 | log.setLevel(logging.ERROR) 15 | log.addHandler(logging.NullHandler()) 16 | 17 | log_handler = logging.handlers.RotatingFileHandler(LOGFILE_NAME, backupCount=5, mode='w') 18 | 19 | log_handler.setFormatter(logging.Formatter("%(asctime)s [%(name)s:%(levelname)s] %(message)s")) 20 | for logger_name in ['test_utils', 'OpenLbr']: 21 | temp = logging.getLogger(logger_name) 22 | temp.setLevel(logging.DEBUG) 23 | temp.addHandler(log_handler) 24 | 25 | # ============================ defines ========================================= 26 | 27 | # ============================ fixtures ======================================== 28 | 29 | # ===== expected_buf2int 30 | 31 | EXPECTED_BUF2INT = [ 32 | # buf int 33 | json.dumps(([0x01, 0x02], 0x0102)), 34 | json.dumps(([0xaa, 0xbb], 0xaabb)), 35 | ] 36 | 37 | 38 | @pytest.fixture(params=EXPECTED_BUF2INT) 39 | def expected_buf2int(request): 40 | return request.param 41 | 42 | 43 | # ===== expected_hex2buf 44 | 45 | EXPECTED_HEX2BUF = [ 46 | # hex buf 47 | json.dumps(('abcd', [0xab, 0xcd])), 48 | json.dumps(('', [])), 49 | json.dumps(('aa', [0xaa])), 50 | ] 51 | 52 | 53 | @pytest.fixture(params=EXPECTED_HEX2BUF) 54 | def expected_hex2buf(request): 55 | return request.param 56 | 57 | 58 | # ===== expected_byteinverse 59 | 60 | EXPECTED_BYTEINVERSE = [ 61 | # b b_inverse 62 | json.dumps((0x01, 0x80)), 63 | json.dumps((0x02, 0x40)), 64 | json.dumps((0x04, 0x20)), 65 | json.dumps((0x81, 0x81)), 66 | ] 67 | 68 | 69 | @pytest.fixture(params=EXPECTED_BYTEINVERSE) 70 | def expected_byteinverse(request): 71 | return request.param 72 | 73 | 74 | # ===== expected_format_ipv6 75 | 76 | EXPECTED_FORMAT_IPv6 = [ 77 | json.dumps( 78 | ( 79 | [ # list 80 | 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 81 | 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 82 | ], 83 | '123:4567:89ab:cdef:fedc:ba98:7654:3210', # string 84 | ), 85 | ), 86 | json.dumps( 87 | ( 88 | [ # list 89 | 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0xcd, 0xef, 90 | 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 91 | ], 92 | '123:4567:0:cdef:fedc:ba98:7654:3210', # string 93 | ), 94 | ), 95 | json.dumps( 96 | ( 97 | [ # list 98 | 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 99 | 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 100 | ], 101 | '123:4567:0:0:fedc:ba98:7654:3210', # string 102 | ), 103 | ), 104 | ] 105 | 106 | 107 | @pytest.fixture(params=EXPECTED_FORMAT_IPv6) 108 | def expected_format_ipv6(request): 109 | return request.param 110 | 111 | 112 | # ============================ helpers ========================================= 113 | 114 | # ============================ tests =========================================== 115 | 116 | def test_buf2int(expected_buf2int): 117 | (exp_buf, exp_int) = json.loads(expected_buf2int) 118 | 119 | assert buf2int(exp_buf) == exp_int 120 | 121 | 122 | def test_hex2buf(expected_hex2buf): 123 | (exp_hex, exp_buf) = json.loads(expected_hex2buf) 124 | exp_hex = str(exp_hex) 125 | 126 | assert hex2buf(exp_hex) == exp_buf 127 | 128 | 129 | def test_byteinverse(expected_byteinverse): 130 | (b, b_inverse) = json.loads(expected_byteinverse) 131 | 132 | assert byteinverse(b) == b_inverse 133 | assert byteinverse(b_inverse) == b 134 | 135 | 136 | def test_format_ipv6_addr(expected_format_ipv6): 137 | (ipv6_list, ipv6_string) = json.loads(expected_format_ipv6) 138 | 139 | log.info(ipv6_string) 140 | 141 | assert format_ipv6_addr(ipv6_list) == ipv6_string 142 | -------------------------------------------------------------------------------- /tests/server_client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwsn-berkeley/openvisualizer/de673bc00077f20987abf7d950399179993e6219/tests/server_client/__init__.py -------------------------------------------------------------------------------- /tests/server_client/test_client.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from click.testing import CliRunner 3 | 4 | from openvisualizer.client.main import cli 5 | 6 | 7 | def test_start_cli(): 8 | """ Tests entry point command. """ 9 | runner = CliRunner() 10 | result = runner.invoke(cli, []) 11 | assert result.exit_code == 0 12 | assert "Usage:" in result.output 13 | 14 | 15 | def test_list_support_rpcs(server): 16 | runner = CliRunner() 17 | result = runner.invoke(cli, ['list-methods']) 18 | assert result.exit_code == 0 19 | assert "boot_motes" in result.output 20 | 21 | 22 | def test_list_support_rpcs_booted(server_booted): 23 | runner = CliRunner() 24 | result = runner.invoke(cli, ['list-methods']) 25 | assert result.exit_code == 0 26 | assert "boot_motes" in result.output 27 | 28 | 29 | def test_get_motes(server): 30 | runner = CliRunner() 31 | result = runner.invoke(cli, ['motes']) 32 | assert result.exit_code == 0 33 | assert "Attached motes (address | port):" in result.output 34 | 35 | 36 | def test_get_motes_booted(server_booted): 37 | runner = CliRunner() 38 | result = runner.invoke(cli, ['motes']) 39 | assert result.exit_code == 0 40 | assert "Attached motes (address | port):" in result.output 41 | 42 | 43 | @pytest.mark.parametrize('opts', ['--mote=all', '--mote=0001']) 44 | def test_boot_command(server, opts): 45 | runner = CliRunner() 46 | result = runner.invoke(cli, ['boot', opts]) 47 | assert result.exit_code == 0 48 | 49 | 50 | @pytest.mark.parametrize('opts', ['--mote=all', '--mote=0001']) 51 | def test_boot_command_booted(server_booted, opts): 52 | runner = CliRunner() 53 | result = runner.invoke(cli, ['boot', opts]) 54 | assert result.exit_code == 0 55 | 56 | 57 | @pytest.mark.parametrize( 58 | 'opts, out', 59 | [('', 'No DAG root configured'), 60 | ('0001', 'Ok!'), ('emulated1', 'Ok!'), 61 | ('6846', 'Unknown port or address'), 62 | ('emulated', 'Unknown port or address')]) 63 | def test_set_root_booted(server_booted, opts, out): 64 | runner = CliRunner() 65 | if opts != '': 66 | result = runner.invoke(cli, ['root', opts]) 67 | else: 68 | result = runner.invoke(cli, ['root']) 69 | assert result.exit_code == 0 70 | assert out in result.output 71 | 72 | 73 | @pytest.mark.parametrize( 74 | 'opts, out', 75 | [('', 'No DAG root configured'), 76 | ('0001', 'Unknown port or address'), ('emulated1', 'Could not set None as root'), 77 | ('6846', 'Unknown port or address'), ('emulated', 'Unknown port or address')]) 78 | def test_set_root(server, opts, out): 79 | runner = CliRunner() 80 | if opts != '': 81 | result = runner.invoke(cli, ['root', opts]) 82 | else: 83 | result = runner.invoke(cli, ['root']) 84 | assert result.exit_code == 0 85 | assert out in result.output 86 | 87 | 88 | def test_start_view(server): 89 | runner = CliRunner() 90 | result = runner.invoke(cli, ['view']) 91 | assert result.exit_code == 0 92 | assert 'Usage:' in result.output 93 | -------------------------------------------------------------------------------- /tests/server_client/test_server.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | from subprocess import Popen 5 | 6 | from pytest import mark, param 7 | 8 | LOG_FILE = 'openv-server.log' 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | @mark.parametrize('option, logs', 14 | [('', ['Discovered serial-port(s):']), # testing plain openv-server boot (1) 15 | ('--sim=2', # boot openv-server with 2 emulated motes (2) 16 | ['- simulation = 2', 17 | '- simulation topology = Pister-hack', 18 | '- auto-boot sim motes = True', 19 | '[OPENWSN] booted']), 20 | # verify emulated moted do not boot 21 | param('--sim=2 --no-boot', ['[OPENWSN] booted'], marks=mark.xfail(reason='motes not booted')), 22 | ('--simtopo=linear', # specify a simulation topology but do not set a number of emulated motes (3) 23 | ['Simulation topology specified but no --sim= given, switching to hardware mode', 24 | 'Discovered serial-port(s):']), 25 | ('--sim=5 --root=0001', # set simulation with five motes and specify root mote (4) 26 | ['Setting mote 0001 as root', 27 | '- simulation topology = Pister-hack', 28 | '- auto-boot sim motes = True', 29 | '- simulation = 5']), 30 | ('--sim=2 --root=0001 --no-boot', # do not boot motes but try to set root (5) 31 | ['Cannot set root', 32 | '- set root = 0001', 33 | '- auto-boot sim motes = False']), 34 | ('--sim=2 --no-boot --load-topology=0002-star.json', # specify sim options and load topology (6) 35 | ['option might be overwritten by the configuration in \'0002-star.json\'']), 36 | ]) 37 | def test_start_server(option, logs): 38 | try: 39 | os.remove(LOG_FILE) 40 | except OSError: 41 | logger.warning('Could not remove old log file: {}'.format(LOG_FILE)) 42 | pass 43 | opts = option.split(' ') 44 | arguments = ['openv-server'] 45 | if opts != ['']: 46 | arguments.extend(opts) 47 | p = Popen(arguments) 48 | 49 | # give the server time to boot 50 | time.sleep(3) 51 | 52 | # kill server 53 | p.terminate() 54 | 55 | with open(LOG_FILE, 'r') as f: 56 | output = "".join(f.readlines()) 57 | for log in logs: 58 | assert log in output 59 | assert 'Starting RPC server' in output 60 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | .git, 4 | __pycache__ 5 | 6 | max-line-length = 120 7 | 8 | disable = 9 | C813, 10 | C815, 11 | C816 12 | --------------------------------------------------------------------------------