├── .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 |
19 |
20 |
83 |
84 |
85 |
86 |
Network Connectivity
87 |
88 |
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 |
16 |
17 |
56 |
57 |
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 |
19 |
20 |
83 |
84 |
85 |
86 |
Current RPL Routing
87 |
88 |
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 | 
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 |
--------------------------------------------------------------------------------