diff --git a/.github/workflows/pi-gen-action.yml b/.github/workflows/pi-gen-action.yml new file mode 100644 index 0000000..4415152 --- /dev/null +++ b/.github/workflows/pi-gen-action.yml @@ -0,0 +1,117 @@ +- uses: usimd/pi-gen-action@v1 + with: + # Compression to apply on final image (either "none", "zip", "xz" or "gz"). + compression: zip + + # Compression level to be used. From 0 to 9 (refer to the tool man page for more + # information on this. Usually 0 is no compression but very fast, up to 9 with the + # best compression but very slow). + compression-level: 6 + + # Disable the renaming of the first user during the first boot. This make it so + # 'username' stays activated. 'username' must be set for this to work. Please be + # aware of the implied security risk of defining a default username and password + # for your devices. + disable-first-boot-user-rename: 1 + + # Additional options to include in PIGEN_DOCKER_OPTS + docker-opts: '' + + # Set whether a NOOBS image should be built as well. If enabled, the output + # directory containing the NOOBS files will be saved as output variable + # 'image-noobs-path'. + enable-noobs: false + + # Enable SSH access to Pi. + enable-ssh: 1 + + # If this feature is enabled, the action will configure pi-gen to not export any + # stage as image but the last one defined in property 'stage-list'. This is + # helpful when building a single image flavor (in contrast to building a + # lite/server and full-blown desktop image), since it speeds up the build process + # significantly. + export-last-stage-only: true + + # Comma or whitespace separated list of additional packages to install on host + # before running pi-gen. Use this list to add any packages your custom stages may + # require. Note that this is not affecting the final image. In order to add + # additional packages, you need to add a respective 'XX-packages' file in your + # custom stage. + extra-host-dependencies: '' + + # Comma or whitespace separated list of additional modules to load on host before + # running pi-gen. If your custom stage requires additional software or kernel + # modules to be loaded, add them here. Note that this is not meant to configure + # modules to be loaded in the target image. + extra-host-modules: '' + + # Token to use for checking out pi-gen repo. + github-token: ${{ github.token }} + + # Host name of the image. + hostname: aryaos + + # Final image name. + image-name: aryaos + + # Default keyboard keymap. + keyboard-keymap: us + + # Default keyboard layout. + keyboard-layout: English (US) + + # Default locale of the system image. + locale: en_US.UTF-8 + + # Password of the intial user account, locked if empty. + password: aryaos415 + + # Path where selected pi-gen ref will be checked out to. If the path does not yet + # exist, it will be created (including its parents). + pi-gen-dir: pi-gen + + # The release name to use in `/etc/issue.txt`. The default should only be used for + # official Raspberry Pi builds. + pi-gen-release: 'AryaOS: An Operating System for Modern Situational Awareness' + + # GitHub repository to fetch pi-gen from, must be a fork from RPi-Distro/pi-gen. + pi-gen-repository: RPi-Distro/pi-gen + + # Release version of pi-gen to use. This can both be a branch or tag name known in + # the pi-gen repository. + pi-gen-version: arm64 + + # The release version to build images against. Valid values are jessie, stretch, + # buster, bullseye, bookworm, and testing. + release: bookworm + + # Setting to `1` will prevent pi-gen from dropping the "capabilities" feature. + # Generating the root filesystem with capabilities enabled and running it from a + # filesystem that does not support capabilities (like NFS) can cause issues. Only + # enable this if you understand what it is. + setfcap: '' + + # List of stage name to execute in given order. Relative and absolute paths to + # custom stage directories are allowed here. Note that by default pi-gen exports + # images in stage2 (lite), stage4 and stage5. You probably want to hook in custom + # stages before one of the exported stages. Otherwise, the action will make sure + # any custom stage will include an image export directive. + stage-list: stage0 stage1 stage2 + + # System timezone. + timezone: UTC + + # Name of the initial user account. + username: pi + + # Print all output from pi-gen. + verbose-output: true + + # Wifi country code of default network to connect to. + wpa-country: US + + # SSID of a default wifi network to connect to. + wpa-essid: AryaOS-WiFi + + # Password of default wifi network to connect to. + wpa-password: aryaos415 diff --git a/Makefile b/Makefile index ce0ca7b..0b9cdd4 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ pi-gen: copy: rsync -va ../aryaos kelp.local:~/src/SNS/ + rsync -va ../aryaos titan2.local:~/src/SNS/ sync: copy @@ -49,4 +50,4 @@ skip5: mkdocs: pip install -r docs/requirements.txt - mkdocs serve \ No newline at end of file + mkdocs serve diff --git a/config b/config index 951ff78..86b4a44 100644 --- a/config +++ b/config @@ -15,24 +15,27 @@ # limitations under the License. # -AOS_FLAVOR="aryaos" -AOS_FLAVOR_ST="AryaOS" +AOS_FLAVOR="AryaOS" -IMG_NAME="aryaos-1.0.0-beta1" +IMG_NAME="aryaos-1.0.0-beta9" PI_GEN_RELEASE="AryaOS: An Operating System for Modern Situational Awareness." TARGET_HOSTNAME="aryaos" FIRST_USER_NAME="pi" FIRST_USER_PASS="aryaos415" -STAGE_LIST="stage0 stage1 stage2 ../stage3-wifi ../stage4-node-red ../stage5-common ../stage6-dump1090 ../stage7-adsbcot" +STAGE_LIST="stage0 stage1 stage2 ../stage3-base ../stage3-wifi ../stage4-node-red ../stage5-common ../stage6-air ../stage7-sea ../stage8-docker" DISABLE_FIRST_BOOT_USER_RENAME=1 RELEASE="bookworm" +WPA_ESSID="AryaOS-WiFi" +WPA_PASSWORD="aryaos415" WPA_COUNTRY="US" LOCALE_DEFAULT="en_US.UTF-8" KEYBOARD_LAYOUT="English (US)" KEYBOARD_KEYMAP="us" ENABLE_SSH=1 +APT_PROXY="http://172.17.2.88:3142" DUMP1090_RECEIVER_SERIAL="stx:1090:0" DUMP978_RECEIVER_SERIAL="stx:978:0" - +COMITUP_WEB_PORT="9080" +AOS_SERVICES="AISCOT ADSBCOT LINCOT nodered" diff --git a/docs/build.md b/docs/build.md index 4d98abe..37207f5 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,17 +1,17 @@ # Download or Build -## Download AirTAK Open Source +## Download AyraOS -* Latest: [R02](https://drive.google.com/file/d/1ZaAzdc1E-pdhytGDDaUfkhIVFpyTmuGk/view?usp=sharing). +* FIXME TK -## Build AirTAK Open Source +## Build AryaOS -The build environment for AirTAK is based on [pi-gen](https://github.com/RPi-Distro/pi-gen). -The build will create a Raspberry Pi OS image, compatible with [Raspberry Pi Imager](https://www.raspberrypi.com/software/) or [Balena Etcher](https://etcher.balena.io/). +The build environment for AryaOS is based on [pi-gen](https://github.com/RPi-Distro/pi-gen). +The build will create a Raspberry Pi OS image, compatible with [Raspberry Pi Imager](https://www.raspberrypi.com/software/). Any SD card larger than 16 GB should suffice to get started, 32 GB recommended. -The AirTAK build procedure is inspired by @deltazero's [kiosk.pi](https://medium.com/@deltazero/making-kioskpi-custom-raspberry-pi-os-image-using-pi-gen-99aac2cd8cb6). +The AryaOS build procedure is inspired by @deltazero's [kiosk.pi](https://medium.com/@deltazero/making-kioskpi-custom-raspberry-pi-os-image-using-pi-gen-99aac2cd8cb6). ### Build Steps @@ -27,4 +27,4 @@ To Update a build: 2. `make skip` to skip base-os build steps. 3. `make buld` to create an image. -N.B.: THe `make build` step will attempt to sudo, and may prompt for a password. \ No newline at end of file +N.B.: The `make build` step will attempt to sudo, and may prompt for a password. \ No newline at end of file diff --git a/docs/build/doctrees/api.doctree b/docs/build/doctrees/api.doctree deleted file mode 100644 index 8357e31..0000000 Binary files a/docs/build/doctrees/api.doctree and /dev/null differ diff --git a/docs/build/doctrees/clients.doctree b/docs/build/doctrees/clients.doctree deleted file mode 100644 index a730796..0000000 Binary files a/docs/build/doctrees/clients.doctree and /dev/null differ diff --git a/docs/build/doctrees/compat.doctree b/docs/build/doctrees/compat.doctree deleted file mode 100644 index 5237332..0000000 Binary files a/docs/build/doctrees/compat.doctree and /dev/null differ diff --git a/docs/build/doctrees/config.doctree b/docs/build/doctrees/config.doctree deleted file mode 100644 index 3d47466..0000000 Binary files a/docs/build/doctrees/config.doctree and /dev/null differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle deleted file mode 100644 index a06ca0d..0000000 Binary files a/docs/build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/build/doctrees/examples.doctree b/docs/build/doctrees/examples.doctree deleted file mode 100644 index 469d439..0000000 Binary files a/docs/build/doctrees/examples.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.CLITool.doctree b/docs/build/doctrees/generated/pytak.CLITool.doctree deleted file mode 100644 index 474e7aa..0000000 Binary files a/docs/build/doctrees/generated/pytak.CLITool.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.QueueWorker.doctree b/docs/build/doctrees/generated/pytak.QueueWorker.doctree deleted file mode 100644 index 8d28868..0000000 Binary files a/docs/build/doctrees/generated/pytak.QueueWorker.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.RXWorker.doctree b/docs/build/doctrees/generated/pytak.RXWorker.doctree deleted file mode 100644 index c115856..0000000 Binary files a/docs/build/doctrees/generated/pytak.RXWorker.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.TXWorker.doctree b/docs/build/doctrees/generated/pytak.TXWorker.doctree deleted file mode 100644 index eb7af42..0000000 Binary files a/docs/build/doctrees/generated/pytak.TXWorker.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.Worker.doctree b/docs/build/doctrees/generated/pytak.Worker.doctree deleted file mode 100644 index 7873574..0000000 Binary files a/docs/build/doctrees/generated/pytak.Worker.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.cli.doctree b/docs/build/doctrees/generated/pytak.cli.doctree deleted file mode 100644 index ec726c9..0000000 Binary files a/docs/build/doctrees/generated/pytak.cli.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.create_udp_client.doctree b/docs/build/doctrees/generated/pytak.create_udp_client.doctree deleted file mode 100644 index 6657c5f..0000000 Binary files a/docs/build/doctrees/generated/pytak.create_udp_client.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.doctree b/docs/build/doctrees/generated/pytak.doctree deleted file mode 100644 index 013dd88..0000000 Binary files a/docs/build/doctrees/generated/pytak.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.protocol_factory.doctree b/docs/build/doctrees/generated/pytak.protocol_factory.doctree deleted file mode 100644 index 9d60eb8..0000000 Binary files a/docs/build/doctrees/generated/pytak.protocol_factory.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.read_pref_package.doctree b/docs/build/doctrees/generated/pytak.read_pref_package.doctree deleted file mode 100644 index fa22e86..0000000 Binary files a/docs/build/doctrees/generated/pytak.read_pref_package.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.rxworker_factory.doctree b/docs/build/doctrees/generated/pytak.rxworker_factory.doctree deleted file mode 100644 index 53d14f2..0000000 Binary files a/docs/build/doctrees/generated/pytak.rxworker_factory.doctree and /dev/null differ diff --git a/docs/build/doctrees/generated/pytak.txworker_factory.doctree b/docs/build/doctrees/generated/pytak.txworker_factory.doctree deleted file mode 100644 index c3ba19b..0000000 Binary files a/docs/build/doctrees/generated/pytak.txworker_factory.doctree and /dev/null differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree deleted file mode 100644 index 91d1154..0000000 Binary files a/docs/build/doctrees/index.doctree and /dev/null differ diff --git a/docs/build/doctrees/install.doctree b/docs/build/doctrees/install.doctree deleted file mode 100644 index 16d3d4f..0000000 Binary files a/docs/build/doctrees/install.doctree and /dev/null differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo deleted file mode 100644 index 8eecdba..0000000 --- a/docs/build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: c61ab6eaf92b43fb4e1a011da43669b6 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_images/atak_screenshot_with_pytak_logo-x25.jpg b/docs/build/html/_images/atak_screenshot_with_pytak_logo-x25.jpg deleted file mode 100644 index 4ae5f1d..0000000 Binary files a/docs/build/html/_images/atak_screenshot_with_pytak_logo-x25.jpg and /dev/null differ diff --git a/docs/build/html/_sources/api.rst.txt b/docs/build/html/_sources/api.rst.txt deleted file mode 100644 index 81f0503..0000000 --- a/docs/build/html/_sources/api.rst.txt +++ /dev/null @@ -1,18 +0,0 @@ -API -=== - -.. autosummary:: - :toctree: generated - - pytak - pytak.Worker - pytak.TXWorker - pytak.RXWorker - pytak.QueueWorker - pytak.CLITool - pytak.create_udp_client - pytak.protocol_factory - pytak.txworker_factory - pytak.rxworker_factory - pytak.cli - pytak.read_pref_package \ No newline at end of file diff --git a/docs/build/html/_sources/clients.rst.txt b/docs/build/html/_sources/clients.rst.txt deleted file mode 100644 index c0b0c07..0000000 --- a/docs/build/html/_sources/clients.rst.txt +++ /dev/null @@ -1,28 +0,0 @@ -Clients -======= - -PyTAK is used by many CoT & TAK gateways: - -`aiscot `_ - Automatic Identification System (AIS) to COT Gateway. Transforms marine AIS position messages to COT PLI Events. - -`adsbcot `_ - Automatic Dependent Surveillance-Broadcast (ADS-B) to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`adsbxcot `_ - ADS-B Exchange to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`stratuxcot `_ - Stratux ADS-B to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`aprscot `_ - Automatic Packet Reporting System (APRS) to COT Gateway. Transforms APRS position messages to COT PLI Events. - -`spotcot `_ - Globalstar SPOT to COT Gateway. Transforms Spot satellite position messages to COT PLI Events. - -`inrcot `_ - Garmin inReach to COT Gateway. Transforms inReach satellite position messages to COT PLI Events. - -`zellocot `_ - ZelloWork to COT Gateway. Transforms ZelloWork user locations to COT PLI Events. diff --git a/docs/build/html/_sources/compat.rst.txt b/docs/build/html/_sources/compat.rst.txt deleted file mode 100644 index 2e99fe1..0000000 --- a/docs/build/html/_sources/compat.rst.txt +++ /dev/null @@ -1,139 +0,0 @@ -Compatibility -============= - -Clients & Servers ------------------ -PyTAK is used in mission criticial environments, every day, across all official -`TAK Products `_: - -* `WinTAK `_ -* `ATAK `_ -* `iTAK `_ -* `TAKX `_ -* `TAK Server `_ - -PyTAK has been tested and is compatible with many situational awareness (SA) & common -operating picture (COP) systems: - -* `taky `_ -* `Free TAK Server (FTS/FreeTAKServer) `_ -* RaptorX -* COPERS - -I/O & Network Protocols ------------------------ -PyTAK supports the following I/O & network protocols: - -* TLS Unicast: ``tls://host:port`` (see `TLS Support `_ section below) -* TCP Unicast: ``tcp://host:port`` -* UDP Multicast: ``udp://group:port`` (aka Mesh SA) -* UDP Unicast: ``udp://host:port`` -* UDP Broadcast: ``udp+broadcast://network:port`` -* UDP Write-only: ``udp+wo://host:port`` -* stdout or stderr: ``log://stdout`` or ``log://stderr`` - -Python 3.6+ ------------ - -PyTAK requires Python 3.6 or above and WILL NOT work on Python versions below 3.6. It -should run on almost any platform that supports Python 3.6+, including Linux, Windows, -Raspberry Pi, Android, et al. - - -FreeTAKServer -------------- - -FTS (Free TAK Server) has built-in anti-Denial-of-Service (DoS) support, which -restricts the number of CoT Events a client can send to a listening TCP Port. -Currently this FTS feature cannot be disabled or changed, so clients must meter -their input speed. - -To use a PyTAK-based client with FTS, set the ``FTS_COMPAT`` configuration parameter -to ``True``. This will cause the PyTAK client to sleep a random number of seconds -between transmitting CoT to a FTS server:: - - FTS_COMPAT = True - -Alternatively you can specify a static sleep period by setting ``PYTAK_SLEEP`` to an -integer number of seconds:: - - PYTAK_SLEEP = 3 - - -TAK Protocol Payload - Version 1 (Protobuf) -------------------------------------------- - - Version 1 of the TAK Protocol Payload is a Google Protocol Buffer based - payload. Each Payload consists of one (and only one) - atakmap::commoncommo::v1::TakMessage message which is serialized using - Google protocol buffers version 3. - - Source: https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/protocol.txt - -PyTAK natively sends and receives "TAK Protocol Payload - Version 0", aka plain XML. If -you'd like to receive & decode "Version 1" protobuf with PyTAK, install the optional -`takproto `_ Python module:: - -When installing PyTAK:: - - $ python3 -m pip install pytak[with_takproto] - -Alternative, installing from a Debian package:: - - $ TK TK - -Here is an example of receiving & decoding "Version 1" using ``takproto``. - -N.B. The data type returned from this implementation differs from that of the -"Version 0" implementation (``bytes`` vs ``object``):: - - #!/usr/bin/env python3 - - import asyncio - - from configparser import ConfigParser - - import takproto - - import pytak - - - class MyRXWorker(pytak.RXWorker): - async def readcot(self): - if hasattr(self.reader, 'readuntil'): - cot = await self.reader.readuntil("".encode("UTF-8")) - elif hasattr(self.reader, 'recv'): - cot, src = await self.reader.recv() - tak_v1 = takproto.parse_proto(cot) - if tak_v1 != -1: - cot = tak_v1 - return cot - - - async def my_setup(clitool) -> None: - reader, writer = await pytak.protocol_factory(clitool.config) - write_worker = pytak.TXWorker(clitool.tx_queue, clitool.config, writer) - read_worker = MyRXWorker(clitool.rx_queue, clitool.config, reader) - clitool.add_task(write_worker) - clitool.add_task(read_worker) - - - async def main(): - """ - The main definition of your program, sets config params and - adds your serializer to the asyncio task list. - """ - config = ConfigParser() - config["mycottool"] = {"COT_URL": "udp://239.2.3.1:6969"} - config = config["mycottool"] - - # Initializes worker queues and tasks. - clitool = pytak.CLITool(config) - await my_setup(clitool) - - # Start all tasks. - await clitool.run() - - - if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file diff --git a/docs/build/html/_sources/config.rst.txt b/docs/build/html/_sources/config.rst.txt deleted file mode 100644 index c603737..0000000 --- a/docs/build/html/_sources/config.rst.txt +++ /dev/null @@ -1,97 +0,0 @@ -Configuration -============= - -PyTAK's configuration parameters can be set two ways: - -1. In an INI-style configuration file, typically ``config.ini`` -2. As environment variables. - -PyTAK has the following built-in configuration parameters: - -.. describe:: COT_URL (optional) - - Destination for Cursor on Target messages. Defaults to ``udp://239.2.3.1:6969`` (ATAK Multicast UDP / Mesh SA Default) - -TAK_PROTO - Sets TAK Protocol to use for CoT output, one of: 0 (XML), 2 (Mesh), 2 (Stream). - - * Default: 0 (XML) - -DEBUG - Sets debug-level logging. - - * Default: False - -FTS_COMPAT - If set, implements random-seconds-sleep period to avoid FTS DoS protections. - - * Default: False - -PYTAK_SLEEP - If set, implements given sleep period of seconds between emitting CoT Events. - - * Default: 0 - -PREF_PACKAGE - (If PyTAK is installed with optional with_crypto support.) - - PyTAK supports importing TAK Data Packages containing TAK Server connection settings, - TLS certificates, etc. - - To use a .zip file with PyTAK, set the ``PREF_PACKAGE`` config parameter to the - path to the .zip file. - - For example, given a Pref Package named ``ADSB3_FIRE.zip``, you could either: - - Using ``config.ini``: Add the line ``PREF_PACKAGE=ADSB3_FIRE.zip`` - - Using the commandline of a utility: Add the argument ``-p DSB3_FIRE.zip`` - - -TLS Support ------------ - -PyTAK can send & receive data over TLS by setting the following configuration -parameters (at a minimum):: - -1) Specify ``tls://`` in the CoT Destination URL, for example: ``tls://takserver.example.com:8089`` -2) Specify the TLS Cert in ``PYTAK_TLS_CLIENT_CERT``. - -Client Certificates, Client Key, CA Certificate & Key must be specified in PEM format. - -*N.B*: Encrypted private keys are not supported and must be saved in clear-text: ``openssl rsa -in my_cert.key.pem -out my_cert-nopass.key.pem`` - -PYTAK_TLS_CLIENT_CERT - Path to a file containing the Client Certificate for PyTAK. File must be - unencrypted plain-text PEM. - - This file can contain both the Client Cert & Client Key, or the Client Cert alone. In - the later case (cert alone), ``PYTAK_TLS_CLIENT_KEY`` must be set to the Client Key. - - For example, to connect to a TAK Server listening for TLS on port 8089:: - - PYTAK_TLS_CLIENT_CERT=client_cert_and_key.pem - COT_URL=tls://tak.example.com:8089 - -**Optional TLS Configuration** - -PYTAK_TLS_CLIENT_KEY - Path to a file containing the Client Private Key for the associated - ``PYTAK_TLS_CLIENT_CERT``. File must be unencrypted plain-text PEM. - -PYTAK_TLS_DONT_VERIFY - Disable destination TLS Certificate Verification. Will print a WARNING if set. - -PYTAK_TLS_DONT_CHECK_HOSTNAME - Disable destination TLS Certificate Common Name (CN) Verification. Will print a - WARNING if set. - -PYTAK_TLS_CLIENT_CAFILE - Path to a file containing the CA Trust Store to use for remote certificate verification. - -PYTAK_TLS_CLIENT_CIPHERS - Colon (":") seperated list of TLS Cipher Suites to allow. - - For example: ``PYTAK_TLS_CLIENT_CIPHERS=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384`` - - * Default: ``ALL`` diff --git a/docs/build/html/_sources/examples.rst.txt b/docs/build/html/_sources/examples.rst.txt deleted file mode 100644 index 71ec525..0000000 --- a/docs/build/html/_sources/examples.rst.txt +++ /dev/null @@ -1,82 +0,0 @@ -Examples -======== - -The following Python 3.7+ code example creates a TAK Client that generates ``takPong`` -CoT every 20 seconds, and sends them to a TAK Server at -``tcp://takserver.example.com:8087`` (plain / clear TCP). - -* For secure TLS, see `TLS Support `_ below. - -To run this example as-is, save the following code-block out to a file named -``example.py`` and run the command ``python3 example.py``:: - - #!/usr/bin/env python3 - - import asyncio - import xml.etree.ElementTree as ET - - from configparser import ConfigParser - - import pytak - - - class MySerializer(pytak.QueueWorker): - """ - Defines how you process or generate your Cursor-On-Target Events. - From there it adds the COT Events to a queue for TX to a COT_URL. - """ - - async def handle_data(self, data): - """ - Handles pre-COT data and serializes to COT Events, then puts on queue. - """ - event = data - await self.put_queue(event) - - async def run(self, number_of_iterations=-1): - """ - Runs the loop for processing or generating pre-COT data. - """ - while 1: - data = tak_pong() - await self.handle_data(data) - await asyncio.sleep(20) - - - def tak_pong(): - """ - Generates a simple takPong COT Event. - """ - root = ET.Element("event") - root.set("version", "2.0") - root.set("type", "t-x-d-d") - root.set("uid", "takPong") - root.set("how", "m-g") - root.set("time", pytak.cot_time()) - root.set("start", pytak.cot_time()) - root.set("stale", pytak.cot_time(3600)) - return ET.tostring(root) - - - async def main(): - """ - The main definition of your program, sets config params and - adds your serializer to the asyncio task list. - """ - config = ConfigParser() - config["mycottool"] = {"COT_URL": "tcp://takserver.example.com:8087"} - config = config["mycottool"] - - # Initializes worker queues and tasks. - clitool = pytak.CLITool(config) - await clitool.setup() - - # Add your serializer to the asyncio task list. - clitool.add_tasks(set([MySerializer(clitool.tx_queue, config)])) - - # Start all tasks. - await clitool.run() - - - if __name__ == "__main__": - asyncio.run(main()) diff --git a/docs/build/html/_sources/generated/pytak.CLITool.rst.txt b/docs/build/html/_sources/generated/pytak.CLITool.rst.txt deleted file mode 100644 index 0de8114..0000000 --- a/docs/build/html/_sources/generated/pytak.CLITool.rst.txt +++ /dev/null @@ -1,36 +0,0 @@ -pytak.CLITool -============= - -.. currentmodule:: pytak - -.. autoclass:: CLITool - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~CLITool.__init__ - ~CLITool.add_task - ~CLITool.add_tasks - ~CLITool.create_workers - ~CLITool.hello_event - ~CLITool.run - ~CLITool.run_task - ~CLITool.run_tasks - ~CLITool.setup - - - - - - .. rubric:: Attributes - - .. autosummary:: - - ~CLITool.config - - \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.QueueWorker.rst.txt b/docs/build/html/_sources/generated/pytak.QueueWorker.rst.txt deleted file mode 100644 index 18d3a86..0000000 --- a/docs/build/html/_sources/generated/pytak.QueueWorker.rst.txt +++ /dev/null @@ -1,26 +0,0 @@ -pytak.QueueWorker -================= - -.. currentmodule:: pytak - -.. autoclass:: QueueWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~QueueWorker.__init__ - ~QueueWorker.fts_compat - ~QueueWorker.handle_data - ~QueueWorker.put_queue - ~QueueWorker.run - - - - - - \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.RXWorker.rst.txt b/docs/build/html/_sources/generated/pytak.RXWorker.rst.txt deleted file mode 100644 index fcdd15a..0000000 --- a/docs/build/html/_sources/generated/pytak.RXWorker.rst.txt +++ /dev/null @@ -1,26 +0,0 @@ -pytak.RXWorker -============== - -.. currentmodule:: pytak - -.. autoclass:: RXWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~RXWorker.__init__ - ~RXWorker.fts_compat - ~RXWorker.handle_data - ~RXWorker.readcot - ~RXWorker.run - - - - - - \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.TXWorker.rst.txt b/docs/build/html/_sources/generated/pytak.TXWorker.rst.txt deleted file mode 100644 index 2e7a780..0000000 --- a/docs/build/html/_sources/generated/pytak.TXWorker.rst.txt +++ /dev/null @@ -1,26 +0,0 @@ -pytak.TXWorker -============== - -.. currentmodule:: pytak - -.. autoclass:: TXWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~TXWorker.__init__ - ~TXWorker.fts_compat - ~TXWorker.handle_data - ~TXWorker.run - ~TXWorker.send_data - - - - - - \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.Worker.rst.txt b/docs/build/html/_sources/generated/pytak.Worker.rst.txt deleted file mode 100644 index 7d67ac2..0000000 --- a/docs/build/html/_sources/generated/pytak.Worker.rst.txt +++ /dev/null @@ -1,25 +0,0 @@ -pytak.Worker -============ - -.. currentmodule:: pytak - -.. autoclass:: Worker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~Worker.__init__ - ~Worker.fts_compat - ~Worker.handle_data - ~Worker.run - - - - - - \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.cli.rst.txt b/docs/build/html/_sources/generated/pytak.cli.rst.txt deleted file mode 100644 index f275cb9..0000000 --- a/docs/build/html/_sources/generated/pytak.cli.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.cli -========= - -.. currentmodule:: pytak - -.. autofunction:: cli \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.create_udp_client.rst.txt b/docs/build/html/_sources/generated/pytak.create_udp_client.rst.txt deleted file mode 100644 index ad64cb7..0000000 --- a/docs/build/html/_sources/generated/pytak.create_udp_client.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.create\_udp\_client -========================= - -.. currentmodule:: pytak - -.. autofunction:: create_udp_client \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.protocol_factory.rst.txt b/docs/build/html/_sources/generated/pytak.protocol_factory.rst.txt deleted file mode 100644 index bd4c3fe..0000000 --- a/docs/build/html/_sources/generated/pytak.protocol_factory.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.protocol\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: protocol_factory \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.read_pref_package.rst.txt b/docs/build/html/_sources/generated/pytak.read_pref_package.rst.txt deleted file mode 100644 index 8e915be..0000000 --- a/docs/build/html/_sources/generated/pytak.read_pref_package.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.read\_pref\_package -========================= - -.. currentmodule:: pytak - -.. autofunction:: read_pref_package \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.rst.txt b/docs/build/html/_sources/generated/pytak.rst.txt deleted file mode 100644 index 9911b45..0000000 --- a/docs/build/html/_sources/generated/pytak.rst.txt +++ /dev/null @@ -1,23 +0,0 @@ -pytak -===== - -.. automodule:: pytak - - - - - - - - - - - - - - - - - - - diff --git a/docs/build/html/_sources/generated/pytak.rxworker_factory.rst.txt b/docs/build/html/_sources/generated/pytak.rxworker_factory.rst.txt deleted file mode 100644 index b7db5cb..0000000 --- a/docs/build/html/_sources/generated/pytak.rxworker_factory.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.rxworker\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: rxworker_factory \ No newline at end of file diff --git a/docs/build/html/_sources/generated/pytak.txworker_factory.rst.txt b/docs/build/html/_sources/generated/pytak.txworker_factory.rst.txt deleted file mode 100644 index 5f96457..0000000 --- a/docs/build/html/_sources/generated/pytak.txworker_factory.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytak.txworker\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: txworker_factory \ No newline at end of file diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt deleted file mode 100644 index 8c30a39..0000000 --- a/docs/build/html/_sources/index.rst.txt +++ /dev/null @@ -1,38 +0,0 @@ -.. image:: ../atak_screenshot_with_pytak_logo-x25.jpg - :alt: ATAK Screenshot with PyTAK Logo. - -Python Team Awareness Kit Documentation -======================================= - -Python Team Awareness Kit (PyTAK) is a Python Module for creating TAK clients, -servers & gateways and includes classes for handling Cursor on Target (CoT) & non-CoT -data, as well as functions for serializing CoT data, and sending and receiving CoT data -over a network. - -Check out the :doc:`install` further information on installing this project. Configuration -parameters and their behavior can be found in the :doc:`config`. - - -Contents --------- -.. toctree:: - :maxdepth: 2 - - install - config - compat - clients - examples - api - -.. seealso:: - - `pytak source code on Github `_ - -Indices and tables -================== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -(pytak |version|) \ No newline at end of file diff --git a/docs/build/html/_sources/install.rst.txt b/docs/build/html/_sources/install.rst.txt deleted file mode 100644 index 2725f10..0000000 --- a/docs/build/html/_sources/install.rst.txt +++ /dev/null @@ -1,66 +0,0 @@ - -Installation -============ - -Debian, Ubuntu, Raspberry Pi ----------------------------- - -PyTAK is distributed as a Debian package (``.deb``). PyTAK should be compatible with -most contemporary system-Python versions from Python 3.6 onward. The `Advanced Package -Tool (apt) `_ is used to install this -and other related packages. - -To install PyTAK, download the pytak package and install using apt:: - - $ sudo apt update -y - $ wget https://github.com/snstac/pytak/releases/latest/download/python3-pytak_latest_all.deb - $ sudo apt install -f ./python3-pytak_latest_all.deb - -Data Package Support -#################### - -To install PyTAK with Deta Package support, you must also install the Python -`cryptography `_ module using apt:: - - $ sudo apt update -y - $ sudo apt install -y python3-cryptography - -TAK Protocol Version 1 (protobuf) Support -######################################### - -To install PyTAK with TAK Protocol Version 1 (protobuf) support, you must also install -the Python TAKProto module `takproto `_. - -To install the TAKProto module, download the takproto package and install using apt:: - - $ sudo apt update -y - $ wget https://github.com/snstak/takproto/releases/latest/download/python3-takproto_latest_all.deb - $ sudo apt install -f ./python3-takproto_latest_all.deb - - -Alternative Installation ------------------------- - -You can install from PyPI or from source. Both of these methods will require manual -installation of additional libraries. - -1a. Debian, Ubuntu, Raspberry Pi: Install `LibFFI `_:: - - $ sudo apt update -y - $ sudo apt install libffi-dev - -1b. RedHat, CentOS: Install `LibFFI `_:: - - $ sudo yum install libffi-devel - -2a. Install PyTAK from the Python Package Index:: - - $ python3 -m pip install pytak - $ python3 -m pip install pytak[with_crypto] - $ python3 -m pip install pytak[with_takproto] - -2b. Install PyTAK from source:: - - $ git clone https://github.com/snstac/pytak.git - $ cd pytak/ - $ python3 -m pip install . diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css deleted file mode 100644 index 7577acb..0000000 --- a/docs/build/html/_static/basic.css +++ /dev/null @@ -1,903 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/build/html/_static/css/badge_only.css b/docs/build/html/_static/css/badge_only.css deleted file mode 100644 index c718cee..0000000 --- a/docs/build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.eot b/docs/build/html/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.svg b/docs/build/html/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/docs/build/html/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf b/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff b/docs/build/html/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 b/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold-italic.woff b/docs/build/html/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 b/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold.woff b/docs/build/html/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold.woff2 b/docs/build/html/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal-italic.woff b/docs/build/html/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 b/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal.woff b/docs/build/html/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal.woff2 b/docs/build/html/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/theme.css b/docs/build/html/_static/css/theme.css deleted file mode 100644 index c03c88f..0000000 --- a/docs/build/html/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js deleted file mode 100644 index d06a71d..0000000 --- a/docs/build/html/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js deleted file mode 100644 index 3462e59..0000000 --- a/docs/build/html/_static/documentation_options.js +++ /dev/null @@ -1,14 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.1.1', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/docs/build/html/_static/file.png and /dev/null differ diff --git a/docs/build/html/_static/js/badge_only.js b/docs/build/html/_static/js/badge_only.js deleted file mode 100644 index 526d723..0000000 --- a/docs/build/html/_static/js/badge_only.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); \ No newline at end of file diff --git a/docs/build/html/_static/js/html5shiv-printshiv.min.js b/docs/build/html/_static/js/html5shiv-printshiv.min.js deleted file mode 100644 index 2b43bd0..0000000 --- a/docs/build/html/_static/js/html5shiv-printshiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/build/html/_static/js/html5shiv.min.js b/docs/build/html/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674..0000000 --- a/docs/build/html/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/build/html/_static/js/theme.js b/docs/build/html/_static/js/theme.js deleted file mode 100644 index 1fddb6e..0000000 --- a/docs/build/html/_static/js/theme.js +++ /dev/null @@ -1 +0,0 @@ -!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/docs/build/html/_static/minus.png b/docs/build/html/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/docs/build/html/_static/minus.png and /dev/null differ diff --git a/docs/build/html/_static/plus.png b/docs/build/html/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/docs/build/html/_static/plus.png and /dev/null differ diff --git a/docs/build/html/_static/pygments.css b/docs/build/html/_static/pygments.css deleted file mode 100644 index 08bec68..0000000 --- a/docs/build/html/_static/pygments.css +++ /dev/null @@ -1,74 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #9C6500 } /* Comment.Preproc */ -.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #E40000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #008400 } /* Generic.Inserted */ -.highlight .go { color: #717171 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #687822 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #767600 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mb { color: #666666 } /* Literal.Number.Bin */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #0000FF } /* Name.Function.Magic */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .vm { color: #19177C } /* Name.Variable.Magic */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/build/html/_static/searchtools.js b/docs/build/html/_static/searchtools.js deleted file mode 100644 index 97d56a7..0000000 --- a/docs/build/html/_static/searchtools.js +++ /dev/null @@ -1,566 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - - const [docName, title, anchor, descr, score, _filename] = item; - - let listItem = document.createElement("li"); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = docUrlRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = docUrlRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms) - ); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = _( - `Search finished, found ${resultCount} page(s) matching the search query.` - ); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent !== undefined) return docContent.textContent; - console.warn( - "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - /** - * execute search (requires search index to be loaded) - */ - query: (query) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - // array of [docname, title, anchor, descr, score, filename] - let results = []; - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - let score = Math.round(100 * queryLower.length / title.length) - results.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id] of foundEntries) { - let score = Math.round(100 * queryLower.length / entry.length) - results.push([ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // lookup as object - objectTerms.forEach((term) => - results.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); - - // now sort the results by score (in opposite order of appearance, since the - // display function below uses pop() to retrieve items) and then - // alphabetically - results.sort((a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; - }); - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - results = results.reverse(); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord) && !terms[word]) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord) && !titleTerms[word]) - arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); - }); - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) - fileMap.get(file).push(word); - else fileMap.set(file, [word]); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords) => { - const text = Search.htmlToText(htmlText); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/docs/build/html/_static/sphinx_highlight.js b/docs/build/html/_static/sphinx_highlight.js deleted file mode 100644 index aae669d..0000000 --- a/docs/build/html/_static/sphinx_highlight.js +++ /dev/null @@ -1,144 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - parent.insertBefore( - span, - parent.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(SphinxHighlight.highlightSearchWords); -_ready(SphinxHighlight.initEscapeListener); diff --git a/docs/build/html/api.html b/docs/build/html/api.html deleted file mode 100644 index 3192279..0000000 --- a/docs/build/html/api.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - API — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

API

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

pytak

Python Team Awareness Kit (PyTAK) Module.

pytak.Worker(queue[, config])

Meta class for all other Worker Classes.

pytak.TXWorker(queue, config, writer)

Works data queue and hands off to Protocol Workers.

pytak.RXWorker(queue, config, reader)

Async receive (input) queue worker.

pytak.QueueWorker(queue, config)

Read non-CoT Messages from an async network client.

pytak.CLITool(config[, tx_queue, rx_queue])

Wrapper Object for CLITools.

pytak.create_udp_client(url[, iface, local_addr])

Create an AsyncIO UDP network client for Unicast, Broadcast & Multicast.

pytak.protocol_factory(config)

Create a network connection class instance.

pytak.txworker_factory(queue, config)

Create a PyTAK TXWorker based on URL parameters.

pytak.rxworker_factory(queue, config)

Create a PyTAK TXWorker based on URL parameters.

pytak.cli(app_name)

Abstract implementation of a Command Line Interface (CLI).

pytak.read_pref_package(pref_package)

Read a pref package / data package of preferences.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/clients.html b/docs/build/html/clients.html deleted file mode 100644 index 4a4afae..0000000 --- a/docs/build/html/clients.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - Clients — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Clients

-

PyTAK is used by many CoT & TAK gateways:

-
-
aiscot

Automatic Identification System (AIS) to COT Gateway. Transforms marine AIS position messages to COT PLI Events.

-
-
adsbcot

Automatic Dependent Surveillance-Broadcast (ADS-B) to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events.

-
-
adsbxcot

ADS-B Exchange to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events.

-
-
stratuxcot

Stratux ADS-B to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events.

-
-
aprscot

Automatic Packet Reporting System (APRS) to COT Gateway. Transforms APRS position messages to COT PLI Events.

-
-
spotcot

Globalstar SPOT to COT Gateway. Transforms Spot satellite position messages to COT PLI Events.

-
-
inrcot

Garmin inReach to COT Gateway. Transforms inReach satellite position messages to COT PLI Events.

-
-
zellocot

ZelloWork to COT Gateway. Transforms ZelloWork user locations to COT PLI Events.

-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/compat.html b/docs/build/html/compat.html deleted file mode 100644 index 31e0e9e..0000000 --- a/docs/build/html/compat.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - - Compatibility — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Compatibility

-
-

Clients & Servers

-

PyTAK is used in mission criticial environments, every day, across all official -TAK Products:

- -

PyTAK has been tested and is compatible with many situational awareness (SA) & common -operating picture (COP) systems:

- -
-
-

I/O & Network Protocols

-

PyTAK supports the following I/O & network protocols:

-
    -
  • TLS Unicast: tls://host:port (see TLS Support section below)

  • -
  • TCP Unicast: tcp://host:port

  • -
  • UDP Multicast: udp://group:port (aka Mesh SA)

  • -
  • UDP Unicast: udp://host:port

  • -
  • UDP Broadcast: udp+broadcast://network:port

  • -
  • UDP Write-only: udp+wo://host:port

  • -
  • stdout or stderr: log://stdout or log://stderr

  • -
-
-
-

Python 3.6+

-

PyTAK requires Python 3.6 or above and WILL NOT work on Python versions below 3.6. It -should run on almost any platform that supports Python 3.6+, including Linux, Windows, -Raspberry Pi, Android, et al.

-
-
-

FreeTAKServer

-

FTS (Free TAK Server) has built-in anti-Denial-of-Service (DoS) support, which -restricts the number of CoT Events a client can send to a listening TCP Port. -Currently this FTS feature cannot be disabled or changed, so clients must meter -their input speed.

-

To use a PyTAK-based client with FTS, set the FTS_COMPAT configuration parameter -to True. This will cause the PyTAK client to sleep a random number of seconds -between transmitting CoT to a FTS server:

-
FTS_COMPAT = True
-
-
-

Alternatively you can specify a static sleep period by setting PYTAK_SLEEP to an -integer number of seconds:

-
PYTAK_SLEEP = 3
-
-
-
-
-

TAK Protocol Payload - Version 1 (Protobuf)

-
-

Version 1 of the TAK Protocol Payload is a Google Protocol Buffer based -payload. Each Payload consists of one (and only one) -atakmap::commoncommo::v1::TakMessage message which is serialized using -Google protocol buffers version 3.

-

Source: https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/protocol.txt

-
-

PyTAK natively sends and receives “TAK Protocol Payload - Version 0”, aka plain XML. If -you’d like to receive & decode “Version 1” protobuf with PyTAK, install the optional -takproto Python module:

-

When installing PyTAK:

-
$ python3 -m pip install pytak[with_takproto]
-
-
-

Alternative, installing from a Debian package:

-
$ TK TK
-
-
-

Here is an example of receiving & decoding “Version 1” using takproto.

-

N.B. The data type returned from this implementation differs from that of the -“Version 0” implementation (bytes vs object):

-
#!/usr/bin/env python3
-
-import asyncio
-
-from configparser import ConfigParser
-
-import takproto
-
-import pytak
-
-
-class MyRXWorker(pytak.RXWorker):
-    async def readcot(self):
-        if hasattr(self.reader, 'readuntil'):
-            cot = await self.reader.readuntil("</event>".encode("UTF-8"))
-        elif hasattr(self.reader, 'recv'):
-            cot, src = await self.reader.recv()
-        tak_v1 = takproto.parse_proto(cot)
-        if tak_v1 != -1:
-            cot = tak_v1
-        return cot
-
-
-async def my_setup(clitool) -> None:
-    reader, writer = await pytak.protocol_factory(clitool.config)
-    write_worker = pytak.TXWorker(clitool.tx_queue, clitool.config, writer)
-    read_worker = MyRXWorker(clitool.rx_queue, clitool.config, reader)
-    clitool.add_task(write_worker)
-    clitool.add_task(read_worker)
-
-
-async def main():
-    """
-    The main definition of your program, sets config params and
-    adds your serializer to the asyncio task list.
-    """
-    config = ConfigParser()
-    config["mycottool"] = {"COT_URL": "udp://239.2.3.1:6969"}
-    config = config["mycottool"]
-
-    # Initializes worker queues and tasks.
-    clitool = pytak.CLITool(config)
-    await my_setup(clitool)
-
-    # Start all tasks.
-    await clitool.run()
-
-
-if __name__ == "__main__":
-    asyncio.run(main())
-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/config.html b/docs/build/html/config.html deleted file mode 100644 index e4f0842..0000000 --- a/docs/build/html/config.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - Configuration — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Configuration

-

PyTAK’s configuration parameters can be set two ways:

-
    -
  1. In an INI-style configuration file, typically config.ini

  2. -
  3. As environment variables.

  4. -
-

PyTAK has the following built-in configuration parameters:

-
-
-COT_URL (optional)
-

Destination for Cursor on Target messages. Defaults to udp://239.2.3.1:6969 (ATAK Multicast UDP / Mesh SA Default)

-
- -
-
TAK_PROTO

Sets TAK Protocol to use for CoT output, one of: 0 (XML), 2 (Mesh), 2 (Stream).

-
    -
  • Default: 0 (XML)

  • -
-
-
DEBUG

Sets debug-level logging.

-
    -
  • Default: False

  • -
-
-
FTS_COMPAT

If set, implements random-seconds-sleep period to avoid FTS DoS protections.

-
    -
  • Default: False

  • -
-
-
PYTAK_SLEEP

If set, implements given sleep period of seconds between emitting CoT Events.

-
    -
  • Default: 0

  • -
-
-
PREF_PACKAGE

(If PyTAK is installed with optional with_crypto support.)

-

PyTAK supports importing TAK Data Packages containing TAK Server connection settings, -TLS certificates, etc.

-

To use a .zip file with PyTAK, set the PREF_PACKAGE config parameter to the -path to the .zip file.

-

For example, given a Pref Package named ADSB3_FIRE.zip, you could either:

-

Using config.ini: Add the line PREF_PACKAGE=ADSB3_FIRE.zip

-

Using the commandline of a utility: Add the argument -p DSB3_FIRE.zip

-
-
-
-

TLS Support

-

PyTAK can send & receive data over TLS by setting the following configuration -parameters (at a minimum):

-
    -
  1. Specify tls:// in the CoT Destination URL, for example: tls://takserver.example.com:8089

  2. -
  3. Specify the TLS Cert in PYTAK_TLS_CLIENT_CERT.

  4. -
-

Client Certificates, Client Key, CA Certificate & Key must be specified in PEM format.

-

N.B: Encrypted private keys are not supported and must be saved in clear-text: openssl rsa -in my_cert.key.pem -out my_cert-nopass.key.pem

-
-
PYTAK_TLS_CLIENT_CERT

Path to a file containing the Client Certificate for PyTAK. File must be -unencrypted plain-text PEM.

-

This file can contain both the Client Cert & Client Key, or the Client Cert alone. In -the later case (cert alone), PYTAK_TLS_CLIENT_KEY must be set to the Client Key.

-

For example, to connect to a TAK Server listening for TLS on port 8089:

-
PYTAK_TLS_CLIENT_CERT=client_cert_and_key.pem
-COT_URL=tls://tak.example.com:8089
-
-
-
-
-

Optional TLS Configuration

-
-
PYTAK_TLS_CLIENT_KEY

Path to a file containing the Client Private Key for the associated -PYTAK_TLS_CLIENT_CERT. File must be unencrypted plain-text PEM.

-
-
PYTAK_TLS_DONT_VERIFY

Disable destination TLS Certificate Verification. Will print a WARNING if set.

-
-
PYTAK_TLS_DONT_CHECK_HOSTNAME

Disable destination TLS Certificate Common Name (CN) Verification. Will print a -WARNING if set.

-
-
PYTAK_TLS_CLIENT_CAFILE

Path to a file containing the CA Trust Store to use for remote certificate verification.

-
-
PYTAK_TLS_CLIENT_CIPHERS

Colon (“:”) seperated list of TLS Cipher Suites to allow.

-

For example: PYTAK_TLS_CLIENT_CIPHERS=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384

-
    -
  • Default: ALL

  • -
-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/examples.html b/docs/build/html/examples.html deleted file mode 100644 index f8f6040..0000000 --- a/docs/build/html/examples.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - Examples — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Examples

-

The following Python 3.7+ code example creates a TAK Client that generates takPong -CoT every 20 seconds, and sends them to a TAK Server at -tcp://takserver.example.com:8087 (plain / clear TCP).

- -

To run this example as-is, save the following code-block out to a file named -example.py and run the command python3 example.py:

-
#!/usr/bin/env python3
-
-import asyncio
-import xml.etree.ElementTree as ET
-
-from configparser import ConfigParser
-
-import pytak
-
-
-class MySerializer(pytak.QueueWorker):
-    """
-    Defines how you process or generate your Cursor-On-Target Events.
-    From there it adds the COT Events to a queue for TX to a COT_URL.
-    """
-
-    async def handle_data(self, data):
-        """
-        Handles pre-COT data and serializes to COT Events, then puts on queue.
-        """
-        event = data
-        await self.put_queue(event)
-
-    async def run(self, number_of_iterations=-1):
-        """
-        Runs the loop for processing or generating pre-COT data.
-        """
-        while 1:
-            data = tak_pong()
-            await self.handle_data(data)
-            await asyncio.sleep(20)
-
-
-def tak_pong():
-    """
-    Generates a simple takPong COT Event.
-    """
-    root = ET.Element("event")
-    root.set("version", "2.0")
-    root.set("type", "t-x-d-d")
-    root.set("uid", "takPong")
-    root.set("how", "m-g")
-    root.set("time", pytak.cot_time())
-    root.set("start", pytak.cot_time())
-    root.set("stale", pytak.cot_time(3600))
-    return ET.tostring(root)
-
-
-async def main():
-    """
-    The main definition of your program, sets config params and
-    adds your serializer to the asyncio task list.
-    """
-    config = ConfigParser()
-    config["mycottool"] = {"COT_URL": "tcp://takserver.example.com:8087"}
-    config = config["mycottool"]
-
-    # Initializes worker queues and tasks.
-    clitool = pytak.CLITool(config)
-    await clitool.setup()
-
-    # Add your serializer to the asyncio task list.
-    clitool.add_tasks(set([MySerializer(clitool.tx_queue, config)]))
-
-    # Start all tasks.
-    await clitool.run()
-
-
-if __name__ == "__main__":
-    asyncio.run(main())
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.CLITool.html b/docs/build/html/generated/pytak.CLITool.html deleted file mode 100644 index 1eb2ef6..0000000 --- a/docs/build/html/generated/pytak.CLITool.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - pytak.CLITool — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.CLITool

-
-
-class pytak.CLITool(config: ConfigParser, tx_queue: Queue | Queue | None = None, rx_queue: Queue | Queue | None = None)
-

Wrapper Object for CLITools.

-
-
-__init__(config: ConfigParser, tx_queue: Queue | Queue | None = None, rx_queue: Queue | Queue | None = None) None
-

Initialize CLITool instance.

-
- -

Methods

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__init__(config[, tx_queue, rx_queue])

Initialize CLITool instance.

add_task(task)

Add the given task to our coroutine task list.

add_tasks(tasks)

Add the given list or set of tasks to our couroutine task list.

create_workers(i_config)

Create and run queue workers with specified config parameters.

hello_event()

Send a 'hello world' style event to the Queue.

run()

Run this Thread and its associated coroutine tasks.

run_task(task)

Run the given coroutine task.

run_tasks([tasks])

Run the given list or set of couroutine tasks.

setup()

Set up CLITool.

-

Attributes

- - - - - - -

config

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.QueueWorker.html b/docs/build/html/generated/pytak.QueueWorker.html deleted file mode 100644 index a5e386b..0000000 --- a/docs/build/html/generated/pytak.QueueWorker.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - pytak.QueueWorker — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.QueueWorker

-
-
-class pytak.QueueWorker(queue: Queue | Queue, config: None | SectionProxy | dict)
-

Read non-CoT Messages from an async network client.

-

(asyncio.Protocol or similar async network client) -Serializes it as COT, and puts it onto an asyncio.Queue.

-

Implementations should handle serializing messages as COT Events, and -putting them onto the event_queue.

-

The event_queue is handled by the pytak.EventWorker Class.

-

pytak([asyncio.Protocol]->[pytak.MessageWorker]->[asyncio.Queue])

-
-
-__init__(queue: Queue | Queue, config: None | SectionProxy | dict) None
-

Initialize a Worker instance.

-
- -

Methods

- - - - - - - - - - - - - - - - - - -

__init__(queue, config)

Initialize a Worker instance.

fts_compat()

Apply FreeTAKServer (FTS) compatibility.

handle_data(data)

Handle data (placeholder method, please override).

put_queue(data[, queue_arg])

Put Data onto the Queue.

run([number_of_iterations])

Run this Thread, reads Data from Queue & passes data to next Handler.

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.RXWorker.html b/docs/build/html/generated/pytak.RXWorker.html deleted file mode 100644 index 9ed9f3b..0000000 --- a/docs/build/html/generated/pytak.RXWorker.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - pytak.RXWorker — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.RXWorker

-
-
-class pytak.RXWorker(queue: Queue | Queue, config: None | SectionProxy | dict, reader: Protocol)
-

Async receive (input) queue worker.

-

Reads events from a pytak.protocol_factory() reader and adds them to -an rx_queue.

-

Most implementations use this to drain an RX buffer on a socket.

-

pytak([asyncio.Protocol]->[pytak.EventReceiver]->[queue.Queue])

-
-
-__init__(queue: Queue | Queue, config: None | SectionProxy | dict, reader: Protocol) None
-

Initialize a RXWorker instance.

-
- -

Methods

- - - - - - - - - - - - - - - - - - -

__init__(queue, config, reader)

Initialize a RXWorker instance.

fts_compat()

Apply FreeTAKServer (FTS) compatibility.

handle_data(data)

Handle data (placeholder method, please override).

readcot()

Read CoT from the wire until we hit an event boundary.

run([number_of_iterations])

Run this worker.

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.TXWorker.html b/docs/build/html/generated/pytak.TXWorker.html deleted file mode 100644 index 5861604..0000000 --- a/docs/build/html/generated/pytak.TXWorker.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - pytak.TXWorker — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.TXWorker

-
-
-class pytak.TXWorker(queue: Queue | Queue, config: None | SectionProxy | dict, writer: Protocol)
-

Works data queue and hands off to Protocol Workers.

-

You should create an TXWorker Instance using the pytak.txworker_factory() -Function.

-

Data is put onto the Queue using a pytak.QueueWorker() instance.

-
-
-__init__(queue: Queue | Queue, config: None | SectionProxy | dict, writer: Protocol) None
-

Initialize a TXWorker instance.

-
- -

Methods

- - - - - - - - - - - - - - - - - - -

__init__(queue, config, writer)

Initialize a TXWorker instance.

fts_compat()

Apply FreeTAKServer (FTS) compatibility.

handle_data(data)

Accept CoT event from CoT event queue and process for writing.

run([number_of_iterations])

Run this Thread, reads Data from Queue & passes data to next Handler.

send_data(data)

Send Data using the appropriate Protocol method.

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.Worker.html b/docs/build/html/generated/pytak.Worker.html deleted file mode 100644 index d15c8af..0000000 --- a/docs/build/html/generated/pytak.Worker.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - pytak.Worker — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.Worker

-
-
-class pytak.Worker(queue: Queue | Queue, config: None | SectionProxy | dict = None)
-

Meta class for all other Worker Classes.

-
-
-__init__(queue: Queue | Queue, config: None | SectionProxy | dict = None) None
-

Initialize a Worker instance.

-
- -

Methods

- - - - - - - - - - - - - - - -

__init__(queue[, config])

Initialize a Worker instance.

fts_compat()

Apply FreeTAKServer (FTS) compatibility.

handle_data(data)

Handle data (placeholder method, please override).

run([number_of_iterations])

Run this Thread, reads Data from Queue & passes data to next Handler.

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.cli.html b/docs/build/html/generated/pytak.cli.html deleted file mode 100644 index e9405a7..0000000 --- a/docs/build/html/generated/pytak.cli.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - pytak.cli — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.cli

-
-
-pytak.cli(app_name: str) None
-

Abstract implementation of a Command Line Interface (CLI).

-
-

Parameters

-
-
app_namestr

Name of the app calling this function.

-
-
-
-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.create_udp_client.html b/docs/build/html/generated/pytak.create_udp_client.html deleted file mode 100644 index 165506a..0000000 --- a/docs/build/html/generated/pytak.create_udp_client.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - pytak.create_udp_client — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.create_udp_client

-
-
-async pytak.create_udp_client(url: ParseResult, iface: str | None = None, local_addr=None) Tuple[DatagramClient | None, DatagramClient]
-

Create an AsyncIO UDP network client for Unicast, Broadcast & Multicast.

-
-

Parameters

-
-
urlParseResult

A parsed fully-qualified URL parsed with urllib.parse.urlparse(). -For example: udp://tak.example.com:4242

-
-
-
-
-

Returns

-
-
DatagramClient

An AsyncIO UDP network stream client.

-
-
-
-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.html b/docs/build/html/generated/pytak.html deleted file mode 100644 index 732f2fb..0000000 --- a/docs/build/html/generated/pytak.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - pytak — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak

-

Python Team Awareness Kit (PyTAK) Module.

-
-
source:
-

<https://github.com/snstac/pytak>

-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.protocol_factory.html b/docs/build/html/generated/pytak.protocol_factory.html deleted file mode 100644 index 0c3dd47..0000000 --- a/docs/build/html/generated/pytak.protocol_factory.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - pytak.protocol_factory — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.protocol_factory

-
-
-async pytak.protocol_factory(config: SectionProxy) Any
-

Create a network connection class instance.

-

Class is for the protocol specified by the COT_URL parameter in the config object.

-
-

Parameters

-
-
configSectionProxy

Configuration parameters & values.

-
-
-
-
-

Returns

-
-
Any

Return value depends on the network protocol.

-
-
-
-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.read_pref_package.html b/docs/build/html/generated/pytak.read_pref_package.html deleted file mode 100644 index 66cd9af..0000000 --- a/docs/build/html/generated/pytak.read_pref_package.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - pytak.read_pref_package — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.read_pref_package

-
-
-pytak.read_pref_package(pref_package: str) dict
-

Read a pref package / data package of preferences.

-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.rxworker_factory.html b/docs/build/html/generated/pytak.rxworker_factory.html deleted file mode 100644 index b107b81..0000000 --- a/docs/build/html/generated/pytak.rxworker_factory.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - pytak.rxworker_factory — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.rxworker_factory

-
-
-async pytak.rxworker_factory(queue: Queue, config: SectionProxy) RXWorker
-

Create a PyTAK TXWorker based on URL parameters.

-
-
Parameters:
-
    -
  • cot_url – URL to COT Destination.

  • -
  • event_queue – asyncio.Queue worker to get events from.

  • -
-
-
Returns:
-

EventWorker or asyncio Protocol

-
-
-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/generated/pytak.txworker_factory.html b/docs/build/html/generated/pytak.txworker_factory.html deleted file mode 100644 index 5419551..0000000 --- a/docs/build/html/generated/pytak.txworker_factory.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - pytak.txworker_factory — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

pytak.txworker_factory

-
-
-async pytak.txworker_factory(queue: Queue, config: SectionProxy) TXWorker
-

Create a PyTAK TXWorker based on URL parameters.

-
-
Parameters:
-
    -
  • cot_url – URL to COT Destination.

  • -
  • event_queue – asyncio.Queue worker to get events from.

  • -
-
-
Returns:
-

EventWorker or asyncio Protocol

-
-
-
- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html deleted file mode 100644 index 7c942c1..0000000 --- a/docs/build/html/genindex.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - Index — PyTAK 0.1.1 documentation - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
- -
- -
- -
-

© Copyright 2023 Sensors & Signals LLC.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/index.html b/docs/build/html/index.html deleted file mode 100644 index bfa6096..0000000 --- a/docs/build/html/index.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - Python Team Awareness Kit Documentation — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- - ATAK Screenshot with PyTAK Logo. -
-

Python Team Awareness Kit Documentation

-

Python Team Awareness Kit (PyTAK) is a Python Module for creating TAK clients, -servers & gateways and includes classes for handling Cursor on Target (CoT) & non-CoT -data, as well as functions for serializing CoT data, and sending and receiving CoT data -over a network.

-

Check out the Installation further information on installing this project. Configuration -parameters and their behavior can be found in the Configuration.

-
-

Contents

- - -
-
-
-

Indices and tables

- -

(pytak 0.1.1)

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/install.html b/docs/build/html/install.html deleted file mode 100644 index 7f7ef3a..0000000 --- a/docs/build/html/install.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - Installation — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Installation

-
-

Debian, Ubuntu, Raspberry Pi

-

PyTAK is distributed as a Debian package (.deb). PyTAK should be compatible with -most contemporary system-Python versions from Python 3.6 onward. The Advanced Package -Tool (apt) is used to install this -and other related packages.

-

To install PyTAK, download the pytak package and install using apt:

-
$ sudo apt update -y
-$ wget https://github.com/snstac/pytak/releases/latest/download/python3-pytak_latest_all.deb
-$ sudo apt install -f ./python3-pytak_latest_all.deb
-
-
-
-

Data Package Support

-

To install PyTAK with Deta Package support, you must also install the Python -cryptography module using apt:

-
$ sudo apt update -y
-$ sudo apt install -y python3-cryptography
-
-
-
-
-

TAK Protocol Version 1 (protobuf) Support

-

To install PyTAK with TAK Protocol Version 1 (protobuf) support, you must also install -the Python TAKProto module takproto.

-

To install the TAKProto module, download the takproto package and install using apt:

-
$ sudo apt update -y
-$ wget https://github.com/snstak/takproto/releases/latest/download/python3-takproto_latest_all.deb
-$ sudo apt install -f ./python3-takproto_latest_all.deb
-
-
-
-
-
-

Alternative Installation

-

You can install from PyPI or from source. Both of these methods will require manual -installation of additional libraries.

-

1a. Debian, Ubuntu, Raspberry Pi: Install LibFFI:

-
$ sudo apt update -y
-$ sudo apt install libffi-dev
-
-
-

1b. RedHat, CentOS: Install LibFFI:

-
$ sudo yum install libffi-devel
-
-
-

2a. Install PyTAK from the Python Package Index:

-
$ python3 -m pip install pytak
-$ python3 -m pip install pytak[with_crypto]
-$ python3 -m pip install pytak[with_takproto]
-
-
-

2b. Install PyTAK from source:

-
$ git clone https://github.com/snstac/pytak.git
-$ cd pytak/
-$ python3 -m pip install .
-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv deleted file mode 100644 index 90f213d..0000000 Binary files a/docs/build/html/objects.inv and /dev/null differ diff --git a/docs/build/html/py-modindex.html b/docs/build/html/py-modindex.html deleted file mode 100644 index 7a0fc8b..0000000 --- a/docs/build/html/py-modindex.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - Python Module Index — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Python Module Index

- -
- p -
- - - - - - - -
 
- p
- pytak -
- - -
-
-
- -
- -
-

© Copyright 2023 Sensors & Signals LLC.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/search.html b/docs/build/html/search.html deleted file mode 100644 index b17d969..0000000 --- a/docs/build/html/search.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - Search — PyTAK 0.1.1 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - - - -
- -
- -
-
-
- -
- -
-

© Copyright 2023 Sensors & Signals LLC.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js deleted file mode 100644 index 596b482..0000000 --- a/docs/build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"docnames": ["api", "clients", "compat", "config", "examples", "generated/pytak", "generated/pytak.CLITool", "generated/pytak.QueueWorker", "generated/pytak.RXWorker", "generated/pytak.TXWorker", "generated/pytak.Worker", "generated/pytak.cli", "generated/pytak.create_udp_client", "generated/pytak.protocol_factory", "generated/pytak.read_pref_package", "generated/pytak.rxworker_factory", "generated/pytak.txworker_factory", "index", "install"], "filenames": ["api.rst", "clients.rst", "compat.rst", "config.rst", "examples.rst", "generated/pytak.rst", "generated/pytak.CLITool.rst", "generated/pytak.QueueWorker.rst", "generated/pytak.RXWorker.rst", "generated/pytak.TXWorker.rst", "generated/pytak.Worker.rst", "generated/pytak.cli.rst", "generated/pytak.create_udp_client.rst", "generated/pytak.protocol_factory.rst", "generated/pytak.read_pref_package.rst", "generated/pytak.rxworker_factory.rst", "generated/pytak.txworker_factory.rst", "index.rst", "install.rst"], "titles": ["API", "Clients", "Compatibility", "Configuration", "Examples", "pytak", "pytak.CLITool", "pytak.QueueWorker", "pytak.RXWorker", "pytak.TXWorker", "pytak.Worker", "pytak.cli", "pytak.create_udp_client", "pytak.protocol_factory", "pytak.read_pref_package", "pytak.rxworker_factory", "pytak.txworker_factory", "Python Team Awareness Kit Documentation", "Installation"], "terms": {"pytak": [1, 2, 3, 4, 17, 18], "worker": [2, 4, 7, 8, 9, 15, 16, 17], "txworker": [2, 15, 16, 17], "rxworker": [2, 15, 17], "queuework": [4, 9, 17], "clitool": [2, 4, 17], "create_udp_cli": 17, "protocol_factori": [2, 8, 17], "txworker_factori": [9, 17], "rxworker_factori": 17, "cli": 17, "read_pref_packag": 17, "i": [1, 3, 4, 7, 9, 13, 17, 18], "us": [1, 2, 3, 8, 9, 18], "mani": [1, 2], "cot": [1, 2, 3, 4, 7, 15, 16, 17], "tak": [1, 3, 4, 12, 17], "gatewai": [1, 17], "aiscot": 1, "automat": 1, "identif": 1, "system": [1, 2, 18], "ai": 1, "transform": 1, "marin": 1, "posit": 1, "messag": [1, 2, 3, 7], "pli": 1, "event": [1, 2, 3, 4, 7, 8, 15, 16], "adsbcot": 1, "depend": [1, 13], "surveil": 1, "broadcast": [1, 2, 12], "ad": 1, "b": [1, 2, 3], "aircraft": 1, "adsbxcot": 1, "exchang": 1, "stratuxcot": 1, "stratux": 1, "aprscot": 1, "packet": 1, "report": 1, "apr": 1, "spotcot": 1, "globalstar": 1, "spot": 1, "satellit": 1, "inrcot": 1, "garmin": 1, "inreach": 1, "zellocot": 1, "zellowork": 1, "user": 1, "locat": 1, "mission": 2, "critici": 2, "environ": [2, 3], "everi": [2, 4], "dai": 2, "across": 2, "all": [2, 3, 4, 10], "offici": 2, "product": 2, "wintak": 2, "atak": [2, 3], "itak": 2, "takx": 2, "ha": [2, 3], "been": 2, "test": 2, "situat": 2, "awar": [2, 5], "sa": [2, 3], "common": [2, 3], "oper": 2, "pictur": 2, "cop": 2, "taki": 2, "free": 2, "ft": [2, 3], "raptorx": 2, "coper": 2, "support": [2, 4, 17], "follow": [2, 3, 4], "tl": [2, 4, 17], "unicast": [2, 12], "host": 2, "port": [2, 3], "see": [2, 4], "section": 2, "below": [2, 4], "tcp": [2, 4], "udp": [2, 3, 12], "multicast": [2, 3, 12], "group": 2, "aka": 2, "mesh": [2, 3], "write": 2, "onli": 2, "wo": 2, "stdout": 2, "stderr": 2, "log": [2, 3], "requir": [2, 18], "abov": 2, "WILL": 2, "NOT": 2, "work": [2, 9], "It": 2, "should": [2, 7, 9, 18], "run": [2, 4], "almost": 2, "ani": [2, 13], "platform": 2, "includ": [2, 17], "linux": 2, "window": 2, "raspberri": [2, 17], "pi": [2, 17], "android": 2, "et": [2, 4], "al": 2, "built": [2, 3], "anti": 2, "denial": 2, "servic": 2, "do": [2, 3], "which": 2, "restrict": 2, "number": 2, "can": [2, 3, 17, 18], "send": [2, 3, 4, 17], "listen": [2, 3], "current": 2, "thi": [2, 3, 4, 8, 11, 17, 18], "featur": 2, "cannot": 2, "disabl": [2, 3], "chang": 2, "so": 2, "must": [2, 3, 18], "meter": 2, "input": [2, 8], "speed": 2, "To": [2, 3, 4, 18], "base": [2, 15, 16], "set": [2, 3, 4], "fts_compat": [2, 3], "configur": [2, 13, 17], "paramet": [2, 3, 15, 16, 17], "true": 2, "caus": 2, "sleep": [2, 3, 4], "random": [2, 3], "second": [2, 3, 4], "between": [2, 3], "transmit": 2, "altern": [2, 17], "you": [2, 3, 4, 9, 18], "specifi": [2, 3, 13], "static": 2, "period": [2, 3], "pytak_sleep": [2, 3], "an": [2, 3, 7, 8, 9, 12], "integ": 2, "googl": 2, "buffer": [2, 8], "each": 2, "consist": 2, "one": [2, 3], "atakmap": 2, "commoncommo": 2, "v1": 2, "takmessag": 2, "serial": [2, 4, 7, 17], "sourc": [2, 5, 17, 18], "http": [2, 5, 18], "github": [2, 5, 17, 18], "com": [2, 3, 4, 5, 12, 18], "deptofdefens": 2, "androidtacticalassaultkit": 2, "civ": 2, "blob": 2, "master": 2, "core": 2, "impl": 2, "txt": 2, "nativ": 2, "receiv": [2, 3, 8, 17], "0": [2, 3, 4, 17], "plain": [2, 3, 4], "xml": [2, 3, 4], "If": [2, 3], "d": [2, 4], "like": 2, "decod": 2, "instal": [2, 3, 17], "option": [2, 3], "takproto": [2, 18], "modul": [2, 5, 17, 18], "when": 2, "python3": [2, 4, 18], "m": [2, 4, 18], "pip": [2, 18], "with_takproto": [2, 18], "from": [2, 4, 7, 8, 15, 16, 18], "debian": [2, 17], "packag": [2, 3, 14], "tk": 2, "here": 2, "exampl": [2, 3, 12, 17], "n": [2, 3], "The": [2, 4, 7, 18], "data": [2, 3, 4, 9, 14, 17], "type": [2, 4], "return": [2, 4, 15, 16], "implement": [2, 3, 7, 8, 11], "differ": 2, "byte": 2, "v": 2, "object": [2, 6, 13], "usr": [2, 4], "bin": [2, 4], "env": [2, 4], "import": [2, 3, 4], "asyncio": [2, 4, 7, 8, 12, 15, 16], "configpars": [2, 4, 6], "class": [2, 4, 6, 7, 8, 9, 10, 13, 17], "myrxwork": 2, "async": [2, 4, 7, 8, 12, 13, 15, 16], "def": [2, 4], "readcot": 2, "self": [2, 4], "hasattr": 2, "reader": [2, 8], "readuntil": 2, "await": [2, 4], "encod": 2, "utf": 2, "8": 2, "elif": 2, "recv": 2, "src": 2, "tak_v1": 2, "parse_proto": 2, "my_setup": 2, "none": [2, 6, 7, 8, 9, 10, 11, 12], "writer": [2, 9], "config": [2, 3, 4, 6, 7, 8, 9, 10, 13, 15, 16], "write_work": 2, "tx_queue": [2, 4, 6], "read_work": 2, "rx_queue": [2, 6, 8], "add_task": [2, 4], "main": [2, 4], "definit": [2, 4], "your": [2, 4], "program": [2, 4], "param": [2, 4], "add": [2, 3, 4, 8], "task": [2, 4], "list": [2, 3, 4], "mycottool": [2, 4], "cot_url": [2, 3, 4, 13, 15, 16], "239": [2, 3], "2": [2, 3, 4], "6969": [2, 3], "initi": [2, 4, 6, 7, 8, 9, 10], "queue": [2, 4, 6, 7, 8, 9, 10, 15, 16], "start": [2, 4], "__name__": [2, 4], "__main__": [2, 4], "": 3, "two": 3, "wai": 3, "In": 3, "ini": 3, "style": 3, "file": [3, 4], "typic": 3, "As": 3, "variabl": 3, "destin": [3, 15, 16], "cursor": [3, 4, 17], "target": [3, 4, 17], "default": 3, "3": [3, 4, 17, 18], "1": [3, 4, 17], "tak_proto": 3, "protocol": [3, 7, 8, 9, 13, 15, 16, 17], "output": 3, "stream": [3, 12], "debug": 3, "level": 3, "fals": 3, "avoid": 3, "protect": 3, "given": 3, "emit": 3, "pref_packag": [3, 14], "with_crypto": [3, 18], "contain": 3, "server": [3, 4, 17], "connect": [3, 13], "certif": 3, "etc": 3, "zip": 3, "path": 3, "For": [3, 4, 12], "pref": [3, 14], "name": [3, 4, 11], "adsb3_fir": 3, "could": 3, "either": 3, "line": [3, 11], "commandlin": 3, "util": 3, "argument": 3, "p": 3, "dsb3_fire": 3, "over": [3, 17], "minimum": 3, "url": [3, 12, 15, 16], "takserv": [3, 4], "8089": 3, "cert": 3, "pytak_tls_client_cert": 3, "client": [3, 4, 7, 12, 17], "kei": 3, "ca": 3, "pem": 3, "format": 3, "encrypt": 3, "privat": 3, "ar": 3, "save": [3, 4], "clear": [3, 4], "text": 3, "openssl": 3, "rsa": 3, "my_cert": 3, "out": [3, 4, 17], "nopass": 3, "unencrypt": 3, "both": [3, 18], "alon": 3, "later": 3, "case": 3, "pytak_tls_client_kei": 3, "client_cert_and_kei": 3, "associ": 3, "pytak_tls_dont_verifi": 3, "verif": 3, "Will": 3, "print": 3, "warn": 3, "pytak_tls_dont_check_hostnam": 3, "cn": 3, "pytak_tls_client_cafil": 3, "trust": 3, "store": 3, "remot": 3, "pytak_tls_client_ciph": 3, "colon": 3, "seper": 3, "cipher": 3, "suit": 3, "allow": 3, "ecdh": 3, "ecdsa": 3, "aes256": 3, "gcm": 3, "sha384": 3, "python": [4, 5, 18], "7": 4, "code": [4, 17], "creat": [4, 9, 12, 13, 15, 16, 17], "gener": 4, "takpong": 4, "20": 4, "them": [4, 7, 8], "8087": 4, "secur": 4, "block": 4, "py": 4, "command": [4, 11], "etre": 4, "elementtre": 4, "myseri": 4, "defin": 4, "how": 4, "process": 4, "On": 4, "tx": 4, "handle_data": 4, "handl": [4, 7, 17], "pre": 4, "put": [4, 7, 9], "put_queu": 4, "number_of_iter": 4, "loop": 4, "while": 4, "tak_pong": 4, "simpl": 4, "root": 4, "element": 4, "version": [4, 17], "t": 4, "x": 4, "uid": 4, "g": 4, "time": 4, "cot_tim": 4, "stale": 4, "3600": 4, "tostr": 4, "setup": 4, "non": [7, 17], "well": 17, "function": [9, 11, 17], "network": [7, 12, 13, 17], "check": 17, "further": 17, "inform": 17, "project": 17, "behavior": 17, "found": 17, "ubuntu": 17, "protobuf": 17, "compat": [17, 18], "o": 17, "6": [17, 18], "freetakserv": 17, "payload": 17, "index": [17, 18], "search": 17, "page": 17, "distribut": 18, "deb": 18, "most": [8, 18], "contemporari": 18, "onward": 18, "advanc": 18, "tool": 18, "apt": 18, "other": [10, 18], "relat": 18, "download": 18, "sudo": 18, "updat": 18, "y": 18, "wget": 18, "snstac": [5, 18], "releas": 18, "latest": 18, "pytak_latest_al": 18, "f": 18, "deta": 18, "also": 18, "cryptographi": 18, "snstak": 18, "takproto_latest_al": 18, "pypi": 18, "method": [6, 7, 8, 9, 10, 18], "manual": 18, "addit": 18, "librari": 18, "1a": 18, "libffi": 18, "dev": 18, "1b": 18, "redhat": 18, "cento": 18, "yum": 18, "devel": 18, "2a": 18, "2b": 18, "git": 18, "clone": 18, "cd": 18, "team": 5, "kit": 5, "author": [], "greg": [], "albrecht": [], "gba": [], "copyright": [], "2023": [], "sensor": [], "signal": [], "llc": [], "licens": [], "apach": [], "full_config": [], "wrapper": 6, "__init__": [6, 7, 8, 9, 10], "instanc": [6, 7, 8, 9, 10, 13], "attribut": 6, "dict": [7, 8, 9, 10, 14], "read": [7, 8, 14], "similar": 7, "onto": [7, 9], "event_queu": [7, 15, 16], "eventwork": [7, 15, 16], "messagework": 7, "drain": 8, "rx": 8, "socket": 8, "eventreceiv": 8, "hand": 9, "off": 9, "meta": 10, "app_nam": 11, "str": [11, 12, 14], "abstract": 11, "interfac": 11, "app": 11, "call": 11, "parseresult": 12, "tupl": 12, "datagramcli": 12, "A": 12, "pars": 12, "fulli": 12, "qualifi": 12, "urllib": 12, "urlpars": 12, "4242": 12, "sectionproxi": [7, 8, 9, 10, 13, 15, 16], "valu": 13, "prefer": 14, "get": [15, 16], "api": 17, "stuff": [], "pythong": [], "last": [], "seealso": [], "_": [], "ifac": 12, "local_addr": 12}, "objects": {"": [[5, 0, 0, "-", "pytak"]], "pytak": [[6, 1, 1, "", "CLITool"], [7, 1, 1, "", "QueueWorker"], [8, 1, 1, "", "RXWorker"], [9, 1, 1, "", "TXWorker"], [10, 1, 1, "", "Worker"], [11, 3, 1, "", "cli"], [12, 3, 1, "", "create_udp_client"], [13, 3, 1, "", "protocol_factory"], [14, 3, 1, "", "read_pref_package"], [15, 3, 1, "", "rxworker_factory"], [16, 3, 1, "", "txworker_factory"]], "pytak.CLITool": [[6, 2, 1, "", "__init__"]], "pytak.QueueWorker": [[7, 2, 1, "", "__init__"]], "pytak.RXWorker": [[8, 2, 1, "", "__init__"]], "pytak.TXWorker": [[9, 2, 1, "", "__init__"]], "pytak.Worker": [[10, 2, 1, "", "__init__"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:function"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "function", "Python function"]}, "titleterms": {"api": 0, "client": [1, 2], "compat": 2, "server": 2, "i": 2, "o": 2, "network": 2, "protocol": [2, 18], "python": [2, 17], "3": 2, "6": 2, "freetakserv": 2, "tak": [2, 18], "payload": 2, "version": [2, 18], "1": [2, 18], "protobuf": [2, 18], "configur": 3, "tl": 3, "support": [3, 18], "exampl": 4, "team": 17, "awar": 17, "kit": 17, "document": 17, "content": 17, "indic": 17, "tabl": 17, "instal": 18, "debian": 18, "ubuntu": 18, "raspberri": 18, "pi": 18, "data": 18, "packag": 18, "altern": 18, "pytak": [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], "clitool": 6, "queuework": 7, "rxworker": 8, "txworker": 9, "worker": 10, "cli": 11, "paramet": [11, 12, 13], "create_udp_cli": 12, "return": [12, 13], "protocol_factori": 13, "read_pref_packag": 14, "rxworker_factori": 15, "txworker_factori": 16, "0": [], "quickstart": []}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.intersphinx": 1, "sphinx": 57}, "alltitles": {"API": [[0, "api"]], "Clients": [[1, "clients"]], "Compatibility": [[2, "compatibility"]], "Clients & Servers": [[2, "clients-servers"]], "I/O & Network Protocols": [[2, "i-o-network-protocols"]], "Python 3.6+": [[2, "python-3-6"]], "FreeTAKServer": [[2, "freetakserver"]], "TAK Protocol Payload - Version 1 (Protobuf)": [[2, "tak-protocol-payload-version-1-protobuf"]], "Configuration": [[3, "configuration"]], "TLS Support": [[3, "tls-support"]], "Examples": [[4, "examples"]], "pytak": [[5, "module-pytak"]], "pytak.CLITool": [[6, "pytak-clitool"]], "pytak.QueueWorker": [[7, "pytak-queueworker"]], "pytak.RXWorker": [[8, "pytak-rxworker"]], "pytak.TXWorker": [[9, "pytak-txworker"]], "pytak.Worker": [[10, "pytak-worker"]], "pytak.cli": [[11, "pytak-cli"]], "Parameters": [[11, "parameters"], [12, "parameters"], [13, "parameters"]], "pytak.create_udp_client": [[12, "pytak-create-udp-client"]], "Returns": [[12, "returns"], [13, "returns"]], "pytak.protocol_factory": [[13, "pytak-protocol-factory"]], "pytak.read_pref_package": [[14, "pytak-read-pref-package"]], "pytak.rxworker_factory": [[15, "pytak-rxworker-factory"]], "pytak.txworker_factory": [[16, "pytak-txworker-factory"]], "Python Team Awareness Kit Documentation": [[17, "python-team-awareness-kit-documentation"]], "Contents": [[17, "contents"]], "Indices and tables": [[17, "indices-and-tables"]], "Installation": [[18, "installation"]], "Debian, Ubuntu, Raspberry Pi": [[18, "debian-ubuntu-raspberry-pi"]], "Data Package Support": [[18, "data-package-support"]], "TAK Protocol Version 1 (protobuf) Support": [[18, "tak-protocol-version-1-protobuf-support"]], "Alternative Installation": [[18, "alternative-installation"]]}, "indexentries": {"module": [[5, "module-pytak"]], "pytak": [[5, "module-pytak"]], "clitool (class in pytak)": [[6, "pytak.CLITool"]], "__init__() (pytak.clitool method)": [[6, "pytak.CLITool.__init__"]], "queueworker (class in pytak)": [[7, "pytak.QueueWorker"]], "__init__() (pytak.queueworker method)": [[7, "pytak.QueueWorker.__init__"]], "rxworker (class in pytak)": [[8, "pytak.RXWorker"]], "__init__() (pytak.rxworker method)": [[8, "pytak.RXWorker.__init__"]], "txworker (class in pytak)": [[9, "pytak.TXWorker"]], "__init__() (pytak.txworker method)": [[9, "pytak.TXWorker.__init__"]], "worker (class in pytak)": [[10, "pytak.Worker"]], "__init__() (pytak.worker method)": [[10, "pytak.Worker.__init__"]], "cli() (in module pytak)": [[11, "pytak.cli"]], "create_udp_client() (in module pytak)": [[12, "pytak.create_udp_client"]], "protocol_factory() (in module pytak)": [[13, "pytak.protocol_factory"]], "read_pref_package() (in module pytak)": [[14, "pytak.read_pref_package"]], "rxworker_factory() (in module pytak)": [[15, "pytak.rxworker_factory"]], "txworker_factory() (in module pytak)": [[16, "pytak.txworker_factory"]]}}) \ No newline at end of file diff --git a/docs/config.md b/docs/config.md index 8a557c2..d7733ef 100644 --- a/docs/config.md +++ b/docs/config.md @@ -2,14 +2,13 @@ ## Web Configuration -Many functions of the AirTAK can be controlled, configured and monitored via the AirTAK -Web page. When connecting directly to the AirTAK in Hotspot mode (via AirTAK-XXXX WiFi Network) you can access the AirTAK Web page by visiting [http://airtak.local](http://airtak.local) from your Chrome or Safari web browser (Android & iOS) or in Edge, Chrome or Safari on you computer. +Many functions of the AryaOS can be controlled, configured and monitored via the AryaOS Web page. When connecting directly to the AryaOS in Hotspot mode (via AryaOS-XXXX WiFi Network) you can access the AryaOS Web page by visiting [http://AryaOS.local](http://AryaOS.local) from your Chrome or Safari web browser (Android & iOS) or in Edge, Chrome or Safari on you computer. ### Connect to WiFI -**N.B.**: If you are connected to AirTAK via the WiFi Hospot (AirTAK-XXXX), reconfiguring the WiFi to connect to another network will terminate your Hospot connection. To reach the AirTAK Web page after this point, you'll need to connect to the same network as AirTAK. +**N.B.**: If you are connected to AryaOS via the WiFi Hospot (AryaOS-XXXX), reconfiguring the WiFi to connect to another network will terminate your Hospot connection. To reach the AryaOS Web page after this point, you'll need to connect to the same network as AryaOS. -1. Connect to the AirTAK WiFi network and browse to http://airtak.local +1. Connect to the AryaOS WiFi network and browse to http://AryaOS.local 2. Click the WiFi configuration option. 3. Enter WiFi credentials and apply. @@ -31,12 +30,12 @@ For advanced users. These steps require familiarity with command-line terminals ### Change default password -The AirTAK OS image contains a user with a default password. It is recommended that the -owner of AirTAK gateway change this password. +The AryaOS image contains a user with a default password. It is recommended that the +owner of AryaOS gateway change this password. To change the default password: -1. SSH into AirTAK: ``ssh pi@airtak.local`` +1. SSH into AryaOS: ``ssh pi@AryaOS.local`` 2. Change the password: ``passwd`` **Please make note of this password. There is no password recovery feature.** @@ -45,29 +44,29 @@ See also: [Raspberry Pi Insecure first user](https://www.raspberrypi.com/news/ra ### Change TAK / CoT Destination -By default, AirTAK sends Cursor on Target messages to the ATAK Mesh SA multicast group & port: ``udp://239.2.3.1:6969`` (expressed as a read-only port via ``udp+wo://...``). +By default, AryaOS sends Cursor on Target messages to the AryaOS Mesh SA multicast group & port: ``udp://239.2.3.1:6969`` (expressed as a read-only port via ``udp+wo://...``). -To send CoT to a different destination, you'll need to SSH into AirTAK and change the +To send CoT to a different destination, you'll need to SSH into AryaOS and change the configuration for ``adsbcot``. -1. SSH into AirTAK: ``ssh pi@airtak.local`` +1. SSH into AryaOS: ``ssh pi@AryaOS.local`` 2. Edit adsbcot's configuration: ``sudo nano /boot/adsbcot-config.txt`` (N.B. This is *not* an INI-style file.) 3. Save & reboot. ### Change dump1090-fa & dump978-fa SDR serial numbers -Pre-assembled AirTAK devices come with SDR serial numbers pre-configured in dump1090-fa and dump978-fa. +Pre-assembled AryaOS devices come with SDR serial numbers pre-configured in dump1090-fa and dump978-fa. You'll need to change the value of the SDR serial numbers configured in dump1090-fa and dump978-fa if: -1. Using a self-assmbled AirTAK device. +1. Using a self-assmbled AryaOS device. 2. There is need to change these values (for example, replacing an SDR). -An AirTAK Web Dashboard method of doing this is under development. See Issue [#21](https://github.com/snstac/airtak/issues/21). +An AryaOS Web Dashboard method of doing this is under development. See Issue [#21](https://github.com/snstac/AryaOS/issues/21). #### Changing dump1090-fa SDR serial number -1. SSH into the AirTAK: ``ssh pi@airtak.local`` +1. SSH into the AryaOS: ``ssh pi@AryaOS.local`` 2. List the serial numbers of the installed SDRs by typing the command: ``rtl_test`` ![Example rtl_test output with 1 SDR.](https://images.squarespace-cdn.com/content/v1/6477cab5986c146297acea21/8d1ecb30-17f4-4225-a7c6-76eca789b645/Screen+Shot+2023-07-08+at+11.48.45+AM.png) @@ -84,7 +83,7 @@ An AirTAK Web Dashboard method of doing this is under development. See Issue [#2 ### Changing dump978-fa SDR serial number -1. SSH into the AirTAK: ``ssh pi@airtak.local`` +1. SSH into the AryaOS: ``ssh pi@AryaOS.local`` 2. List the serial numbers of the installed SDRs by typing the command: ``rtl_test`` 3. Using the Nano text editor, open the dump978-fa configuration file: ``sudo nano /etc/default/dump978-fa`` diff --git a/docs/definitions.md b/docs/definitions.md index 22fc27a..b9157f2 100644 --- a/docs/definitions.md +++ b/docs/definitions.md @@ -1,5 +1,5 @@ ## **NODE_ID** -NODE_ID is a UUID4 generated on initial startup of new AirTAK microSD card image. It is used by several components of the AirTAK to uniquely identify the device running AirTAK. +NODE_ID is a UUID4 generated on initial startup of new AirTAK microSD card image. It is used by several components of the AryaOS to uniquely identify the device running AryaOS. diff --git a/docs/install.md b/docs/install.md index 152e3ca..9d293a9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,12 +1,12 @@ -# Installing AirTAK +# Installing AryaOS -AirTAK should run on any ARM64-based computer, and is tested on the Raspberry Pi 3 & 4 models. To run AirTAK on a Raspberry Pi, a SD card image will need to be downloaded and written to an SD card. Any SD card larger than 16 GB should suffice to get started, 32 GB recommended. +AryaOS should run on any ARM64-based computer, and is tested on the Raspberry Pi 3 & 4 models. To run AryaOS on a Raspberry Pi, a SD card image will need to be downloaded and written to an SD card. Any SD card larger than 16 GB should suffice to get started, 32 GB recommended. ## Raspberry Pi Imager -[Raspberry Pi Imager](raspberrypi.com/software/) is the preferred utility for creating an AirTAK SD card and runs any Windows, Mac or Linux workstation. +[Raspberry Pi Imager](raspberrypi.com/software/) is the preferred utility for creating an AryaOS SD card and runs any Windows, Mac or Linux workstation. -1. Download AirTAK release image to the workstation. +1. Download AryaOS release image to the workstation. 2. Insert an SD card into the workstation. 3. Open Raspberry Pi Imager on the workstation. 4. TK TK @@ -15,21 +15,21 @@ Raspberry Pi Imager Screenshot TK ## balenaEtcher (Balena Ether) -[balenaEther](https://etcher.balena.io/) can be used to create an AirTAK microSD card and runs on any Windows, Mac or Linux workstation. +[balenaEther](https://etcher.balena.io/) can be used to create an AryaOS microSD card and runs on any Windows, Mac or Linux workstation. On the workstation: 1. Download and install [balenaEther](https://etcher.balena.io/). -2. Download the AirTAK microSD card image to your Downloads folder.. TK TK TK +2. Download the AryaOS microSD card image to your Downloads folder.. TK TK TK 3. Insert the target microSD card. 4. Open blaneEtcher. -5. Select **Flash from file** and locate the AirTAK microSD card image in your Downloads folder. +5. Select **Flash from file** and locate the AryaOS microSD card image in your Downloads folder. 6. Select the target microSD card. 7. Click **Flash** ![balenaEther screenshot](install/balenaEther_screenshot.png) -Once balenaEtcher has written the AirTAK microSD card image to the target microSD card, it will verify the image and eject the microSD card from the workstation. +Once balenaEtcher has written the AryaOS microSD card image to the target microSD card, it will verify the image and eject the microSD card from the workstation. On the target device: @@ -38,13 +38,13 @@ On the target device: ## Next Steps -Within a few seconds of power-up, an AirTAK device will flash green and red lights. +Within a few seconds of power-up, an AryaOS device will flash green and red lights. -1. Initial startup of a new AirTAK device (either pre-assembled or self-assembled) will take a 120 seconds. During this time the AirTAK device will resize its file system & generate a unique AirTAK Node ID ([NODE_ID](definitions/#NODE_ID)). -2. After initial startup, all AirTAK devices will startup within 90 seconds. +1. Initial startup of a new AryaOS device (either pre-assembled or self-assembled) will take a 120 seconds. During this time the AryaOS device will resize its file system & generate a unique AryaOS Node ID ([NODE_ID](definitions/#NODE_ID)). +2. After initial startup, all AryaOS devices will startup within 90 seconds. -Once an AirTAK device boots, it will establish a WiFi Hotspot with an SSID resembling *AirTAK-XXXX*, where *XXXX* is the last four characters of the NODE_ID. +Once an AryaOS device boots, it will establish a WiFi Hotspot with an SSID resembling *AryaOS-XXXX*, where *XXXX* is the last four characters of the NODE_ID. -If you've self-assembled an AirTAK device, you will need to manually set the SDR serial numbers for each SDR installed on the device. See [config](config) page. +If you've self-assembled an AryaOS device, you will need to manually set the SDR serial numbers for each SDR installed on the device. See [config](config) page. diff --git a/docs/intro.md b/docs/intro.md index c242e18..1ad4b9b 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -1,13 +1,12 @@ # Introduction -## What does AirTAK Do? +## What does AryaOS Do? Any smartphone or computer running ATAK, WinTAK or iTAK, when paired to an AirTAK gateway, displays aircraft data in native TAK formats. -For example, in this field configuration, a Samsung Galaxy S20 smartphone running ATAK was paired to an AirTAK device carried in a backpack. The AirTAK was powered by a portable USB battery -also in the backpack. No outside internet connectivity (no LTE, no WiFi) was utilized by the smartphone or AirTAK gateway +For example, in this field configuration, a Samsung Galaxy S20 smartphone running ATAK was paired to an AirTAK device carried in a backpack. The AirTAK was powered by a portable USB battery also in the backpack. No outside internet connectivity (no LTE, no WiFi) was utilized by the smartphone or AirTAK gateway ![AirTAK attached to a backpack](media/backpack.png) diff --git a/docs/20230613_182136 copy.jpg b/docs/media/20230613_182136 copy.jpg similarity index 100% rename from docs/20230613_182136 copy.jpg rename to docs/media/20230613_182136 copy.jpg diff --git a/docs/20230613_182136.jpg b/docs/media/20230613_182136.jpg similarity index 100% rename from docs/20230613_182136.jpg rename to docs/media/20230613_182136.jpg diff --git a/docs/20230613_182253 copy.jpg b/docs/media/20230613_182253 copy.jpg similarity index 100% rename from docs/20230613_182253 copy.jpg rename to docs/media/20230613_182253 copy.jpg diff --git a/docs/20230613_182253.jpg b/docs/media/20230613_182253.jpg similarity index 100% rename from docs/20230613_182253.jpg rename to docs/media/20230613_182253.jpg diff --git a/docs/20230613_182312 copy.jpg b/docs/media/20230613_182312 copy.jpg similarity index 100% rename from docs/20230613_182312 copy.jpg rename to docs/media/20230613_182312 copy.jpg diff --git a/docs/20230613_182312.jpg b/docs/media/20230613_182312.jpg similarity index 100% rename from docs/20230613_182312.jpg rename to docs/media/20230613_182312.jpg diff --git a/docs/atak_screenshot_with_pytak_logo-x25.jpg b/docs/media/atak_screenshot_with_pytak_logo-x25.jpg similarity index 100% rename from docs/atak_screenshot_with_pytak_logo-x25.jpg rename to docs/media/atak_screenshot_with_pytak_logo-x25.jpg diff --git a/docs/atak_screenshot_with_pytak_logo.jpg b/docs/media/atak_screenshot_with_pytak_logo.jpg similarity index 100% rename from docs/atak_screenshot_with_pytak_logo.jpg rename to docs/media/atak_screenshot_with_pytak_logo.jpg diff --git a/docs/firstshot.png b/docs/media/firstshot.png similarity index 100% rename from docs/firstshot.png rename to docs/media/firstshot.png diff --git a/docs/pytak_logo-256x264.png b/docs/media/pytak_logo-256x264.png similarity index 100% rename from docs/pytak_logo-256x264.png rename to docs/media/pytak_logo-256x264.png diff --git a/docs/pytak_logo.png b/docs/media/pytak_logo.png similarity index 100% rename from docs/pytak_logo.png rename to docs/media/pytak_logo.png diff --git a/docs/takproto_chart.png b/docs/media/takproto_chart.png similarity index 100% rename from docs/takproto_chart.png rename to docs/media/takproto_chart.png diff --git a/docs/purchase.md b/docs/purchase.md index ab3ab19..86e2c68 100644 --- a/docs/purchase.md +++ b/docs/purchase.md @@ -1 +1 @@ -Order an assembled & tested [AirTAK go-kit](https://www.snstac.com/store/p/airtak-v1). \ No newline at end of file +Order an assembled & tested [AirTAK go-kit](https://www.snstac.com/store/p/airtak-v2). \ No newline at end of file diff --git a/docs/source/api.rst b/docs/source/api.rst deleted file mode 100644 index 81f0503..0000000 --- a/docs/source/api.rst +++ /dev/null @@ -1,18 +0,0 @@ -API -=== - -.. autosummary:: - :toctree: generated - - pytak - pytak.Worker - pytak.TXWorker - pytak.RXWorker - pytak.QueueWorker - pytak.CLITool - pytak.create_udp_client - pytak.protocol_factory - pytak.txworker_factory - pytak.rxworker_factory - pytak.cli - pytak.read_pref_package \ No newline at end of file diff --git a/docs/source/clients.rst b/docs/source/clients.rst deleted file mode 100644 index c0b0c07..0000000 --- a/docs/source/clients.rst +++ /dev/null @@ -1,28 +0,0 @@ -Clients -======= - -PyTAK is used by many CoT & TAK gateways: - -`aiscot `_ - Automatic Identification System (AIS) to COT Gateway. Transforms marine AIS position messages to COT PLI Events. - -`adsbcot `_ - Automatic Dependent Surveillance-Broadcast (ADS-B) to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`adsbxcot `_ - ADS-B Exchange to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`stratuxcot `_ - Stratux ADS-B to COT Gateway. Transforms aircraft ADS-B position messages to COT PLI Events. - -`aprscot `_ - Automatic Packet Reporting System (APRS) to COT Gateway. Transforms APRS position messages to COT PLI Events. - -`spotcot `_ - Globalstar SPOT to COT Gateway. Transforms Spot satellite position messages to COT PLI Events. - -`inrcot `_ - Garmin inReach to COT Gateway. Transforms inReach satellite position messages to COT PLI Events. - -`zellocot `_ - ZelloWork to COT Gateway. Transforms ZelloWork user locations to COT PLI Events. diff --git a/docs/source/compat.rst b/docs/source/compat.rst deleted file mode 100644 index 2e99fe1..0000000 --- a/docs/source/compat.rst +++ /dev/null @@ -1,139 +0,0 @@ -Compatibility -============= - -Clients & Servers ------------------ -PyTAK is used in mission criticial environments, every day, across all official -`TAK Products `_: - -* `WinTAK `_ -* `ATAK `_ -* `iTAK `_ -* `TAKX `_ -* `TAK Server `_ - -PyTAK has been tested and is compatible with many situational awareness (SA) & common -operating picture (COP) systems: - -* `taky `_ -* `Free TAK Server (FTS/FreeTAKServer) `_ -* RaptorX -* COPERS - -I/O & Network Protocols ------------------------ -PyTAK supports the following I/O & network protocols: - -* TLS Unicast: ``tls://host:port`` (see `TLS Support `_ section below) -* TCP Unicast: ``tcp://host:port`` -* UDP Multicast: ``udp://group:port`` (aka Mesh SA) -* UDP Unicast: ``udp://host:port`` -* UDP Broadcast: ``udp+broadcast://network:port`` -* UDP Write-only: ``udp+wo://host:port`` -* stdout or stderr: ``log://stdout`` or ``log://stderr`` - -Python 3.6+ ------------ - -PyTAK requires Python 3.6 or above and WILL NOT work on Python versions below 3.6. It -should run on almost any platform that supports Python 3.6+, including Linux, Windows, -Raspberry Pi, Android, et al. - - -FreeTAKServer -------------- - -FTS (Free TAK Server) has built-in anti-Denial-of-Service (DoS) support, which -restricts the number of CoT Events a client can send to a listening TCP Port. -Currently this FTS feature cannot be disabled or changed, so clients must meter -their input speed. - -To use a PyTAK-based client with FTS, set the ``FTS_COMPAT`` configuration parameter -to ``True``. This will cause the PyTAK client to sleep a random number of seconds -between transmitting CoT to a FTS server:: - - FTS_COMPAT = True - -Alternatively you can specify a static sleep period by setting ``PYTAK_SLEEP`` to an -integer number of seconds:: - - PYTAK_SLEEP = 3 - - -TAK Protocol Payload - Version 1 (Protobuf) -------------------------------------------- - - Version 1 of the TAK Protocol Payload is a Google Protocol Buffer based - payload. Each Payload consists of one (and only one) - atakmap::commoncommo::v1::TakMessage message which is serialized using - Google protocol buffers version 3. - - Source: https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/protocol.txt - -PyTAK natively sends and receives "TAK Protocol Payload - Version 0", aka plain XML. If -you'd like to receive & decode "Version 1" protobuf with PyTAK, install the optional -`takproto `_ Python module:: - -When installing PyTAK:: - - $ python3 -m pip install pytak[with_takproto] - -Alternative, installing from a Debian package:: - - $ TK TK - -Here is an example of receiving & decoding "Version 1" using ``takproto``. - -N.B. The data type returned from this implementation differs from that of the -"Version 0" implementation (``bytes`` vs ``object``):: - - #!/usr/bin/env python3 - - import asyncio - - from configparser import ConfigParser - - import takproto - - import pytak - - - class MyRXWorker(pytak.RXWorker): - async def readcot(self): - if hasattr(self.reader, 'readuntil'): - cot = await self.reader.readuntil("".encode("UTF-8")) - elif hasattr(self.reader, 'recv'): - cot, src = await self.reader.recv() - tak_v1 = takproto.parse_proto(cot) - if tak_v1 != -1: - cot = tak_v1 - return cot - - - async def my_setup(clitool) -> None: - reader, writer = await pytak.protocol_factory(clitool.config) - write_worker = pytak.TXWorker(clitool.tx_queue, clitool.config, writer) - read_worker = MyRXWorker(clitool.rx_queue, clitool.config, reader) - clitool.add_task(write_worker) - clitool.add_task(read_worker) - - - async def main(): - """ - The main definition of your program, sets config params and - adds your serializer to the asyncio task list. - """ - config = ConfigParser() - config["mycottool"] = {"COT_URL": "udp://239.2.3.1:6969"} - config = config["mycottool"] - - # Initializes worker queues and tasks. - clitool = pytak.CLITool(config) - await my_setup(clitool) - - # Start all tasks. - await clitool.run() - - - if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index a123c5e..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,38 +0,0 @@ -# Configuration file for the Sphinx documentation builder. - -# -- Project information - -project = "AirTAK" -copyright = "2023 Sensors & Signals LLC" -author = "Greg Albrecht" - -release = "1.0.0" -version = "1.0.0" - -# -- General configuration - -master_doc = "index" - -extensions = [ - "sphinx.ext.duration", - "sphinx.ext.doctest", - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.intersphinx", - "sphinx.ext.coverage", -] - -intersphinx_mapping = { - "python": ("https://docs.python.org/3/", None), - "sphinx": ("https://www.sphinx-doc.org/en/master/", None), -} -intersphinx_disabled_domains = ["std"] - -templates_path = ["_templates"] - -# -- Options for HTML output - -html_theme = "sphinx_rtd_theme" - -# -- Options for EPUB output -epub_show_urls = "footnote" diff --git a/docs/source/config.rst b/docs/source/config.rst deleted file mode 100644 index c603737..0000000 --- a/docs/source/config.rst +++ /dev/null @@ -1,97 +0,0 @@ -Configuration -============= - -PyTAK's configuration parameters can be set two ways: - -1. In an INI-style configuration file, typically ``config.ini`` -2. As environment variables. - -PyTAK has the following built-in configuration parameters: - -.. describe:: COT_URL (optional) - - Destination for Cursor on Target messages. Defaults to ``udp://239.2.3.1:6969`` (ATAK Multicast UDP / Mesh SA Default) - -TAK_PROTO - Sets TAK Protocol to use for CoT output, one of: 0 (XML), 2 (Mesh), 2 (Stream). - - * Default: 0 (XML) - -DEBUG - Sets debug-level logging. - - * Default: False - -FTS_COMPAT - If set, implements random-seconds-sleep period to avoid FTS DoS protections. - - * Default: False - -PYTAK_SLEEP - If set, implements given sleep period of seconds between emitting CoT Events. - - * Default: 0 - -PREF_PACKAGE - (If PyTAK is installed with optional with_crypto support.) - - PyTAK supports importing TAK Data Packages containing TAK Server connection settings, - TLS certificates, etc. - - To use a .zip file with PyTAK, set the ``PREF_PACKAGE`` config parameter to the - path to the .zip file. - - For example, given a Pref Package named ``ADSB3_FIRE.zip``, you could either: - - Using ``config.ini``: Add the line ``PREF_PACKAGE=ADSB3_FIRE.zip`` - - Using the commandline of a utility: Add the argument ``-p DSB3_FIRE.zip`` - - -TLS Support ------------ - -PyTAK can send & receive data over TLS by setting the following configuration -parameters (at a minimum):: - -1) Specify ``tls://`` in the CoT Destination URL, for example: ``tls://takserver.example.com:8089`` -2) Specify the TLS Cert in ``PYTAK_TLS_CLIENT_CERT``. - -Client Certificates, Client Key, CA Certificate & Key must be specified in PEM format. - -*N.B*: Encrypted private keys are not supported and must be saved in clear-text: ``openssl rsa -in my_cert.key.pem -out my_cert-nopass.key.pem`` - -PYTAK_TLS_CLIENT_CERT - Path to a file containing the Client Certificate for PyTAK. File must be - unencrypted plain-text PEM. - - This file can contain both the Client Cert & Client Key, or the Client Cert alone. In - the later case (cert alone), ``PYTAK_TLS_CLIENT_KEY`` must be set to the Client Key. - - For example, to connect to a TAK Server listening for TLS on port 8089:: - - PYTAK_TLS_CLIENT_CERT=client_cert_and_key.pem - COT_URL=tls://tak.example.com:8089 - -**Optional TLS Configuration** - -PYTAK_TLS_CLIENT_KEY - Path to a file containing the Client Private Key for the associated - ``PYTAK_TLS_CLIENT_CERT``. File must be unencrypted plain-text PEM. - -PYTAK_TLS_DONT_VERIFY - Disable destination TLS Certificate Verification. Will print a WARNING if set. - -PYTAK_TLS_DONT_CHECK_HOSTNAME - Disable destination TLS Certificate Common Name (CN) Verification. Will print a - WARNING if set. - -PYTAK_TLS_CLIENT_CAFILE - Path to a file containing the CA Trust Store to use for remote certificate verification. - -PYTAK_TLS_CLIENT_CIPHERS - Colon (":") seperated list of TLS Cipher Suites to allow. - - For example: ``PYTAK_TLS_CLIENT_CIPHERS=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384`` - - * Default: ``ALL`` diff --git a/docs/source/examples.rst b/docs/source/examples.rst deleted file mode 100644 index 71ec525..0000000 --- a/docs/source/examples.rst +++ /dev/null @@ -1,82 +0,0 @@ -Examples -======== - -The following Python 3.7+ code example creates a TAK Client that generates ``takPong`` -CoT every 20 seconds, and sends them to a TAK Server at -``tcp://takserver.example.com:8087`` (plain / clear TCP). - -* For secure TLS, see `TLS Support `_ below. - -To run this example as-is, save the following code-block out to a file named -``example.py`` and run the command ``python3 example.py``:: - - #!/usr/bin/env python3 - - import asyncio - import xml.etree.ElementTree as ET - - from configparser import ConfigParser - - import pytak - - - class MySerializer(pytak.QueueWorker): - """ - Defines how you process or generate your Cursor-On-Target Events. - From there it adds the COT Events to a queue for TX to a COT_URL. - """ - - async def handle_data(self, data): - """ - Handles pre-COT data and serializes to COT Events, then puts on queue. - """ - event = data - await self.put_queue(event) - - async def run(self, number_of_iterations=-1): - """ - Runs the loop for processing or generating pre-COT data. - """ - while 1: - data = tak_pong() - await self.handle_data(data) - await asyncio.sleep(20) - - - def tak_pong(): - """ - Generates a simple takPong COT Event. - """ - root = ET.Element("event") - root.set("version", "2.0") - root.set("type", "t-x-d-d") - root.set("uid", "takPong") - root.set("how", "m-g") - root.set("time", pytak.cot_time()) - root.set("start", pytak.cot_time()) - root.set("stale", pytak.cot_time(3600)) - return ET.tostring(root) - - - async def main(): - """ - The main definition of your program, sets config params and - adds your serializer to the asyncio task list. - """ - config = ConfigParser() - config["mycottool"] = {"COT_URL": "tcp://takserver.example.com:8087"} - config = config["mycottool"] - - # Initializes worker queues and tasks. - clitool = pytak.CLITool(config) - await clitool.setup() - - # Add your serializer to the asyncio task list. - clitool.add_tasks(set([MySerializer(clitool.tx_queue, config)])) - - # Start all tasks. - await clitool.run() - - - if __name__ == "__main__": - asyncio.run(main()) diff --git a/docs/source/generated/pytak.CLITool.rst b/docs/source/generated/pytak.CLITool.rst deleted file mode 100644 index 0de8114..0000000 --- a/docs/source/generated/pytak.CLITool.rst +++ /dev/null @@ -1,36 +0,0 @@ -pytak.CLITool -============= - -.. currentmodule:: pytak - -.. autoclass:: CLITool - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~CLITool.__init__ - ~CLITool.add_task - ~CLITool.add_tasks - ~CLITool.create_workers - ~CLITool.hello_event - ~CLITool.run - ~CLITool.run_task - ~CLITool.run_tasks - ~CLITool.setup - - - - - - .. rubric:: Attributes - - .. autosummary:: - - ~CLITool.config - - \ No newline at end of file diff --git a/docs/source/generated/pytak.QueueWorker.rst b/docs/source/generated/pytak.QueueWorker.rst deleted file mode 100644 index 18d3a86..0000000 --- a/docs/source/generated/pytak.QueueWorker.rst +++ /dev/null @@ -1,26 +0,0 @@ -pytak.QueueWorker -================= - -.. currentmodule:: pytak - -.. autoclass:: QueueWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~QueueWorker.__init__ - ~QueueWorker.fts_compat - ~QueueWorker.handle_data - ~QueueWorker.put_queue - ~QueueWorker.run - - - - - - \ No newline at end of file diff --git a/docs/source/generated/pytak.RXWorker.rst b/docs/source/generated/pytak.RXWorker.rst deleted file mode 100644 index fcdd15a..0000000 --- a/docs/source/generated/pytak.RXWorker.rst +++ /dev/null @@ -1,26 +0,0 @@ -pytak.RXWorker -============== - -.. currentmodule:: pytak - -.. autoclass:: RXWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~RXWorker.__init__ - ~RXWorker.fts_compat - ~RXWorker.handle_data - ~RXWorker.readcot - ~RXWorker.run - - - - - - \ No newline at end of file diff --git a/docs/source/generated/pytak.TXWorker.rst b/docs/source/generated/pytak.TXWorker.rst deleted file mode 100644 index 2e7a780..0000000 --- a/docs/source/generated/pytak.TXWorker.rst +++ /dev/null @@ -1,26 +0,0 @@ -pytak.TXWorker -============== - -.. currentmodule:: pytak - -.. autoclass:: TXWorker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~TXWorker.__init__ - ~TXWorker.fts_compat - ~TXWorker.handle_data - ~TXWorker.run - ~TXWorker.send_data - - - - - - \ No newline at end of file diff --git a/docs/source/generated/pytak.Worker.rst b/docs/source/generated/pytak.Worker.rst deleted file mode 100644 index 7d67ac2..0000000 --- a/docs/source/generated/pytak.Worker.rst +++ /dev/null @@ -1,25 +0,0 @@ -pytak.Worker -============ - -.. currentmodule:: pytak - -.. autoclass:: Worker - - - .. automethod:: __init__ - - - .. rubric:: Methods - - .. autosummary:: - - ~Worker.__init__ - ~Worker.fts_compat - ~Worker.handle_data - ~Worker.run - - - - - - \ No newline at end of file diff --git a/docs/source/generated/pytak.cli.rst b/docs/source/generated/pytak.cli.rst deleted file mode 100644 index f275cb9..0000000 --- a/docs/source/generated/pytak.cli.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.cli -========= - -.. currentmodule:: pytak - -.. autofunction:: cli \ No newline at end of file diff --git a/docs/source/generated/pytak.create_udp_client.rst b/docs/source/generated/pytak.create_udp_client.rst deleted file mode 100644 index ad64cb7..0000000 --- a/docs/source/generated/pytak.create_udp_client.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.create\_udp\_client -========================= - -.. currentmodule:: pytak - -.. autofunction:: create_udp_client \ No newline at end of file diff --git a/docs/source/generated/pytak.protocol_factory.rst b/docs/source/generated/pytak.protocol_factory.rst deleted file mode 100644 index bd4c3fe..0000000 --- a/docs/source/generated/pytak.protocol_factory.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.protocol\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: protocol_factory \ No newline at end of file diff --git a/docs/source/generated/pytak.read_pref_package.rst b/docs/source/generated/pytak.read_pref_package.rst deleted file mode 100644 index 8e915be..0000000 --- a/docs/source/generated/pytak.read_pref_package.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.read\_pref\_package -========================= - -.. currentmodule:: pytak - -.. autofunction:: read_pref_package \ No newline at end of file diff --git a/docs/source/generated/pytak.rst b/docs/source/generated/pytak.rst deleted file mode 100644 index 9911b45..0000000 --- a/docs/source/generated/pytak.rst +++ /dev/null @@ -1,23 +0,0 @@ -pytak -===== - -.. automodule:: pytak - - - - - - - - - - - - - - - - - - - diff --git a/docs/source/generated/pytak.rxworker_factory.rst b/docs/source/generated/pytak.rxworker_factory.rst deleted file mode 100644 index b7db5cb..0000000 --- a/docs/source/generated/pytak.rxworker_factory.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.rxworker\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: rxworker_factory \ No newline at end of file diff --git a/docs/source/generated/pytak.txworker_factory.rst b/docs/source/generated/pytak.txworker_factory.rst deleted file mode 100644 index 5f96457..0000000 --- a/docs/source/generated/pytak.txworker_factory.rst +++ /dev/null @@ -1,6 +0,0 @@ -pytak.txworker\_factory -======================= - -.. currentmodule:: pytak - -.. autofunction:: txworker_factory \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 4e9ac18..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,40 +0,0 @@ -.. image:: ../atak_screenshot_with_pytak_logo-x25.jpg - :alt: ATAK Screenshot with PyTAK Logo. - - -AirTAK Documentation -==================== - -[AirTAK](https://www.snstac.com/blog/introducing-airtak-v1) is a standalone ADS-B to [TAK](https://www.tak.gov) Gateway. AirTAK Open Source is the purpose-built -operating system used by AirTAK gateways. It is based on Debian, and runs on most ARM64 -small-board computers, including the Raspberry Pi. - -Don't want to roll your own? You can order an assembled & tested [AirTAK go-kit](https://www.snstac.com/store/p/airtak-v1). - -Check out the :doc:`install` further information on installing this project. Configuration -parameters and their behavior can be found in the :doc:`config`. - - -Contents --------- -.. toctree:: - :maxdepth: 2 - - install - config - compat - clients - examples - api - -.. seealso:: - - `AirTAK source code on Github `_ - -Indices and tables -================== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -(airtak |version|) \ No newline at end of file diff --git a/docs/source/install.rst b/docs/source/install.rst deleted file mode 100644 index 2725f10..0000000 --- a/docs/source/install.rst +++ /dev/null @@ -1,66 +0,0 @@ - -Installation -============ - -Debian, Ubuntu, Raspberry Pi ----------------------------- - -PyTAK is distributed as a Debian package (``.deb``). PyTAK should be compatible with -most contemporary system-Python versions from Python 3.6 onward. The `Advanced Package -Tool (apt) `_ is used to install this -and other related packages. - -To install PyTAK, download the pytak package and install using apt:: - - $ sudo apt update -y - $ wget https://github.com/snstac/pytak/releases/latest/download/python3-pytak_latest_all.deb - $ sudo apt install -f ./python3-pytak_latest_all.deb - -Data Package Support -#################### - -To install PyTAK with Deta Package support, you must also install the Python -`cryptography `_ module using apt:: - - $ sudo apt update -y - $ sudo apt install -y python3-cryptography - -TAK Protocol Version 1 (protobuf) Support -######################################### - -To install PyTAK with TAK Protocol Version 1 (protobuf) support, you must also install -the Python TAKProto module `takproto `_. - -To install the TAKProto module, download the takproto package and install using apt:: - - $ sudo apt update -y - $ wget https://github.com/snstak/takproto/releases/latest/download/python3-takproto_latest_all.deb - $ sudo apt install -f ./python3-takproto_latest_all.deb - - -Alternative Installation ------------------------- - -You can install from PyPI or from source. Both of these methods will require manual -installation of additional libraries. - -1a. Debian, Ubuntu, Raspberry Pi: Install `LibFFI `_:: - - $ sudo apt update -y - $ sudo apt install libffi-dev - -1b. RedHat, CentOS: Install `LibFFI `_:: - - $ sudo yum install libffi-devel - -2a. Install PyTAK from the Python Package Index:: - - $ python3 -m pip install pytak - $ python3 -m pip install pytak[with_crypto] - $ python3 -m pip install pytak[with_takproto] - -2b. Install PyTAK from source:: - - $ git clone https://github.com/snstac/pytak.git - $ cd pytak/ - $ python3 -m pip install . diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index e76e978..93605ad 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,5 +1,5 @@ -## No ADS-B Tracks in ATAK +## No ADS-B Tracks in AryaOS -### Enable Mesh SA in ATAK. +### Enable Mesh SA in AryaOS. -From ATAK, open the Settings gear. +From AryaOS, open the Settings gear. diff --git a/stage3-base/00-install/00-packages b/stage3-base/00-install/00-packages new file mode 100644 index 0000000..a1ee80e --- /dev/null +++ b/stage3-base/00-install/00-packages @@ -0,0 +1,59 @@ +gpsd +gpsd-clients + +python3-pip +python3-cryptography + +# libatlas-base-dev +# libzmq3-dev + +# libsoxr0 +# libsoxr-dev + +# libsamplerate0 +# libsamplerate0-dev + +rtl-sdr +librtlsdr-dev + +# libairspy0 +# libairspy-dev + +# libairspyhf1 +# libairspyhf-dev + +# hackrf +# hackrf-firmware +# libhackrf0 +# libhackrf-dev + +# bladerf +# libbladerf-dev +# python3-bladerf + +# libsoapysdr-dev +# libsoapysdr0.8 +# python3-soapysdr +# soapyremote-server +# soapysdr-module-all +# soapysdr-tools + +# gqrx-sdr +# cubicsdr + +# gnuradio-dev +# gr-osmosdr + +tcpdump +netcat-openbsd +nmap +socat +net-tools + +# Needy developer: +git +tmux +vim +curl +wget +build-essential diff --git a/stage3-base/00-install/00-run.sh b/stage3-base/00-install/00-run.sh new file mode 100755 index 0000000..c6f9baa --- /dev/null +++ b/stage3-base/00-install/00-run.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +# AryaOS 00-run.sh +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +install -v -m 755 files/get_throttled.sh "${ROOTFS_DIR}/usr/local/sbin/" + +# Set UUID on first boot +install -v -m 755 files/set_UUID.sh "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 644 files/set_UUID.service "${ROOTFS_DIR}/etc/systemd/system/" + +# AryaOS Env configuration +install -v -m 644 "files/${AOS_FLAVOR:-AryaOS}-config.txt" "${ROOTFS_DIR}/boot/" +install -v -m 755 files/99-AryaOS-Dispatcher "${ROOTFS_DIR}/etc/NetworkManager/dispatcher.d/" +install -v -m 644 files/README-AryaOS.txt "${ROOTFS_DIR}/" + +# ZeroTier +install -v -m 755 files/install_ZT.sh "${ROOTFS_DIR}/usr/local/sbin/" diff --git a/stage7-adsbcot/01-install-python-packages/01-run-chroot.sh b/stage3-base/00-install/01-run-chroot.sh similarity index 69% rename from stage7-adsbcot/01-install-python-packages/01-run-chroot.sh rename to stage3-base/00-install/01-run-chroot.sh index b72cf35..7924814 100755 --- a/stage7-adsbcot/01-install-python-packages/01-run-chroot.sh +++ b/stage3-base/00-install/01-run-chroot.sh @@ -14,11 +14,14 @@ # limitations under the License. # -apt install python3-cryptography +systemctl enable set_UUID +systemctl set-default multi-user + +/usr/local/sbin/install_ZT.sh python3 -m pip install pytak --break-system-packages python3 -m pip install takproto --break-system-packages -python3 -m pip install aircot --break-system-packages -python3 -m pip install adsbcot --break-system-packages -systemctl enable adsbcot +echo "wireshark-common wireshark-common/install-setuid boolean true" | debconf-set-selections +echo "tshark-common tshark-common/install-setuid boolean true" | debconf-set-selections +DEBIAN_FRONTEND=noninteractive apt install tshark -y \ No newline at end of file diff --git a/stage5-common/00-install/files/99-aryaos-dispatcher b/stage3-base/00-install/files/99-AryaOS-Dispatcher similarity index 75% rename from stage5-common/00-install/files/99-aryaos-dispatcher rename to stage3-base/00-install/files/99-AryaOS-Dispatcher index d5bbb68..671da4f 100644 --- a/stage5-common/00-install/files/99-aryaos-dispatcher +++ b/stage3-base/00-install/files/99-AryaOS-Dispatcher @@ -1,5 +1,5 @@ #!/bin/sh -e -# AryaOS 99-aryaos-dispatcher +# AryaOS 99-AryaOS-Dispatcher # # Script to dispatch NetworkManager events for AryaOS. # @@ -16,6 +16,8 @@ # limitations under the License. # +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" + if [ -n "$IP4_NUM_ADDRESSES" ] && [ "$IP4_NUM_ADDRESSES" -gt 0 ]; then ADDRESS_FAMILIES="$ADDRESS_FAMILIES inet" fi @@ -44,7 +46,24 @@ export LOGICAL="$1" export METHOD="NetworkManager" export VERBOSITY="0" -. /boot/aryaos-config.txt +if [ -f $AOS_CONFIG ]; then + . $AOS_CONFIG +fi + +function restart_aos_services() { + for srv in $AOS_SERVICES; do + if [ systemctl is-enabled $srv ]; then + logger "99-AryaOS-Dispatcher restarting $srv" + systemctl restart $srv || exit 0 + fi + done +} + +function set_pytak_mc_addr() { + MC_ADDR="${1:-0.0.0.0}" + sed --follow-symlinks -i -E -e "s/PYTAK_MULTICAST_LOCAL_ADDR.*/PYTAK_MULTICAST_LOCAL_ADDR=$MC_ADDR/" $AOS_CONFIG +} + for i in $ADDRESS_FAMILIES; do export ADDRFAM="$i" @@ -52,25 +71,21 @@ for i in $ADDRESS_FAMILIES; do case "$2" in connectivity-change) if [ "$CONNECTIVITY_STATE" = "LIMITED" ]; then - sed --follow-symlinks -i -E -e "s/PYTAK_MULTICAST_LOCAL_ADDR.*/PYTAK_MULTICAST_LOCAL_ADDR=${WIFI_AP_IP}/" /boot/aryaos-config.txt + set_pytak_mc_addr $WIFI_AP_IP elif [ "$CONNECTIVITY_STATE" = "FULL" ]; then - sed --follow-symlinks -i -E -e "s/PYTAK_MULTICAST_LOCAL_ADDR.*/PYTAK_MULTICAST_LOCAL_ADDR=0.0.0.0/" /boot/aryaos-config.txt + set_pytak_mc_addr fi + restart_aos_services - systemctl restart adsbcot - systemctl restart lincot - systemctl restart nodered ;; up|vpn-up) export MODE="start" export PHASE="post-up" if [ -n "$DHCP4_IP_ADDRESS" && "$PYTAK_MULTICAST_LOCAL_ADDR" != "0.0.0.0" ]; then - sed --follow-symlinks -i -E -e "s/PYTAK_MULTICAST_LOCAL_ADDR.*/PYTAK_MULTICAST_LOCAL_ADDR=$DHCP4_IP_ADDRESS/" /boot/aryaos-config.txt + set_pytak_mc_addr $DHCP4_IP_ADDRESS fi - systemctl restart adsbcot - systemctl restart lincot - systemctl restart nodered + restart_aos_services ;; diff --git a/stage5-common/00-install/files/aryaos-config.txt b/stage3-base/00-install/files/AryaOS-config.txt similarity index 90% rename from stage5-common/00-install/files/aryaos-config.txt rename to stage3-base/00-install/files/AryaOS-config.txt index 44bd7b6..dd1a587 100644 --- a/stage5-common/00-install/files/aryaos-config.txt +++ b/stage3-base/00-install/files/AryaOS-config.txt @@ -1,3 +1,5 @@ +# AryaOS AryaOS-config.txt +# # AryaOS Env configuration file. # # Copyright Sensors & Signals LLC https://www.snstac.com/ @@ -17,3 +19,4 @@ COT_URL=udp+wo://239.2.3.1:6969 PYTAK_MULTICAST_LOCAL_ADDR=10.41.0.1 WIFI_AP_IP=10.41.0.1 NODE_ID="" +AOS_SERVICES="ADSBCOT LINCOT AISCOT nodered" \ No newline at end of file diff --git a/stage5-common/00-install/files/README-AryaOS.txt b/stage3-base/00-install/files/README-AryaOS.txt similarity index 100% rename from stage5-common/00-install/files/README-AryaOS.txt rename to stage3-base/00-install/files/README-AryaOS.txt diff --git a/stage5-common/00-install/files/get_throttled.sh b/stage3-base/00-install/files/get_throttled.sh old mode 100644 new mode 100755 similarity index 99% rename from stage5-common/00-install/files/get_throttled.sh rename to stage3-base/00-install/files/get_throttled.sh index 1660642..56b0222 --- a/stage5-common/00-install/files/get_throttled.sh +++ b/stage3-base/00-install/files/get_throttled.sh @@ -1,4 +1,5 @@ #!/bin/bash +# AryaOS get_throttled.sh # # Source: https://github.com/alwye/get_throttled # diff --git a/stage5-common/00-install/files/install_zt.sh b/stage3-base/00-install/files/install_ZT.sh similarity index 99% rename from stage5-common/00-install/files/install_zt.sh rename to stage3-base/00-install/files/install_ZT.sh index 955128c..af2cf15 100755 --- a/stage5-common/00-install/files/install_zt.sh +++ b/stage3-base/00-install/files/install_ZT.sh @@ -1,4 +1,5 @@ #!/bin/bash +# AryaOS install_ZT.sh <> ${ARYAOS_CONF} + logger "$AOS_CONFIG doesn't exist, initializing." + echo 'NODE_ID=""' >> $AOS_CONFIG fi -if ! grep -qs -e 'NODE_ID' ${ARYAOS_CONF}; then - echo "Adding empty NODE_ID to ${ARYAOS_CONF}" - echo 'NODE_ID=""' >> ${ARYAOS_CONF} +if [ ! grep -qs -e 'NODE_ID' $AOS_CONFIG ]; then + logger "Adding empty NODE_ID to $AOS_CONFIG" + echo 'NODE_ID=""' >> $AOS_CONFIG fi if [ -z "$NODE_ID" ]; then NEW_NODE_ID=$(python3 -c "import uuid;print(str(uuid.uuid4()).upper())") - echo "NODE_ID not set" - sed --follow-symlinks -i -E -e "s/NODE_ID.*/NODE_ID=$NEW_NODE_ID/" ${ARYAOS_CONF} - echo "NODE_ID is now set to: $NEW_NODE_ID" + sed --follow-symlinks -i -E -e "s/NODE_ID.*/NODE_ID=$NEW_NODE_ID/" $AOS_CONFIG + logger "AryaOS NODE_ID is now set to: $NEW_NODE_ID" else exit 64 fi \ No newline at end of file diff --git a/stage7-adsbcot/prerun.sh b/stage3-base/prerun.sh similarity index 88% rename from stage7-adsbcot/prerun.sh rename to stage3-base/prerun.sh index 4b72ef7..bb741e2 100755 --- a/stage7-adsbcot/prerun.sh +++ b/stage3-base/prerun.sh @@ -1,6 +1,7 @@ #!/bin/bash -e +# AryaOS prerun.sh # -# Copyright 2023 Sensors & Signals LLC +# Copyright Sensors & Signals LLC https://www.snstac.com/ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/stage3-wifi/00-install/00-run.sh b/stage3-wifi/00-install/00-run.sh index f34c9a0..71134f9 100755 --- a/stage3-wifi/00-install/00-run.sh +++ b/stage3-wifi/00-install/00-run.sh @@ -14,4 +14,5 @@ # limitations under the License. # -install -v -m 644 files/python-networkmanager-main.zip "${ROOTFS_DIR}/home/pi/" +# FIXME DEPRECATED: Replace old NetworkManager Python module. https://github.com/snstac/aryaos/issues/54 +install -v -m 644 files/NetworkManager.py "${ROOTFS_DIR}/usr/lib/python3/dist-packages/NetworkManager.py" diff --git a/stage3-wifi/00-install/02-run-chroot.sh b/stage3-wifi/00-install/02-run-chroot.sh index 0de9cb4..8a3834c 100755 --- a/stage3-wifi/00-install/02-run-chroot.sh +++ b/stage3-wifi/00-install/02-run-chroot.sh @@ -14,9 +14,4 @@ # limitations under the License. # -# FIXME: Remove when fix is merged to NetworManager -# Apply fix to NetworkManager. -unzip -o /home/pi/python-networkmanager-main.zip -cp python-networkmanager-main/NetworkManager.py /usr/lib/python3/dist-packages/NetworkManager.py - systemctl enable NetworkManager \ No newline at end of file diff --git a/stage3-wifi/00-install/03-run.sh b/stage3-wifi/00-install/03-run.sh index 4f5517f..fc08f38 100755 --- a/stage3-wifi/00-install/03-run.sh +++ b/stage3-wifi/00-install/03-run.sh @@ -14,13 +14,11 @@ # limitations under the License. # -COMITUP_WEB_PORT="9080" -sed --follow-symlinks -i -E -e "s/port=80/port=$COMITUP_WEB_PORT/" "${ROOTFS_DIR}/usr/share/comitup/web/comitupweb.py" - # The main branch of comitup uses a different syntax. -# sed --follow-symlinks -i -E -e "s/SERVER_PORT = 80/SERVER_PORT = $COMITUP_WEB_PORT/" /usr/share/comitup/web/comitupweb.py +# sed --follow-symlinks -i -E -e "s/SERVER_PORT = 80/SERVER_PORT = ${COMITUP_WEB_PORT}/" /usr/share/comitup/web/comitupweb.py +sed --follow-symlinks -i -E -e "s/port=80/port=${COMITUP_WEB_PORT}/" "${ROOTFS_DIR}/usr/share/comitup/web/comitupweb.py" -install -v -m 644 files/comitup.conf "${ROOTFS_DIR}/boot/" -install -v -m 755 files/run_comitup.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 755 files/comitup-callback.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 644 files/comitup.service "${ROOTFS_DIR}/lib/systemd/system/" +install -v -m 644 files/comitup.conf "${ROOTFS_DIR}/boot/" +install -v -m 755 files/run_comitup.sh "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 755 files/comitup-callback.sh "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 644 files/comitup.service "${ROOTFS_DIR}/lib/systemd/system/" diff --git a/stage3-wifi/00-install/files/NetworkManager.py b/stage3-wifi/00-install/files/NetworkManager.py new file mode 100644 index 0000000..fe6b5be --- /dev/null +++ b/stage3-wifi/00-install/files/NetworkManager.py @@ -0,0 +1,1454 @@ +# AryaOS NetworkManager.py +# Fix for https://github.com/snstac/python-networkmanager/issues/1 +# +# NetworkManager - a library to make interacting with the NetworkManager daemon +# easier. +# +# (C)2011-2021 Dennis Kaarsemaker +# License: zlib + +import copy +import dbus +import dbus.service +import os +import six +import socket +import struct +import sys +import time +import warnings +import weakref +import xml.etree.ElementTree as etree + + +class ObjectVanished(Exception): + def __init__(self, obj): + self.obj = obj + super(ObjectVanished, self).__init__(obj.object_path) + + +class SignalDispatcher(object): + def __init__(self): + self.handlers = {} + self.args = {} + self.interfaces = set() + self.setup = False + + def setup_signals(self): + if not self.setup: + bus = dbus.SystemBus() + for interface in self.interfaces: + bus.add_signal_receiver( + self.handle_signal, + dbus_interface=interface, + interface_keyword="interface", + member_keyword="signal", + path_keyword="path", + ) + self.setup = True + self.listen_for_restarts() + + def listen_for_restarts(self): + # If we have a mainloop, listen for disconnections + if not NMDbusInterface.last_disconnect and dbus.get_default_main_loop(): + dbus.SystemBus().add_signal_receiver( + self.handle_restart, "NameOwnerChanged", "org.freedesktop.DBus" + ) + NMDbusInterface.last_disconnect = 1 + + def add_signal_receiver(self, interface, signal, obj, func, args, kwargs): + self.setup_signals() + key = (interface, signal) + if key not in self.handlers: + self.handlers[key] = [] + self.handlers[key].append((obj, func, args, kwargs)) + + def handle_signal(self, *args, **kwargs): + key = (kwargs["interface"], kwargs["signal"]) + skwargs = {} + sargs = [] + if key not in self.handlers: + return + try: + sender = fixups.base_to_python(kwargs["path"]) + for arg, (name, signature) in zip(args, self.args[key]): + if name: + skwargs[name] = fixups.to_python( + type(sender).__name__, kwargs["signal"], name, arg, signature + ) + else: + # Older NetworkManager versions don't supply attribute names. Hope for the best. + sargs.append( + fixups.to_python( + type(sender).__name__, + kwargs["signal"], + None, + arg, + signature, + ) + ) + except dbus.exceptions.DBusException: + # This happens if the sender went away. Tough luck, no signal for you. + return + to_delete = [] + for pos, (match, receiver, rargs, rkwargs) in enumerate(self.handlers[key]): + try: + match == sender + except ObjectVanished: + to_delete.append(pos) + continue + if match == sender: + rkwargs["interface"] = kwargs["interface"] + rkwargs["signal"] = kwargs["signal"] + rkwargs.update(skwargs) + receiver(sender, *(sargs + rargs), **rkwargs) + for pos in reversed(to_delete): + self.handlers[key].pop(pos) + + def handle_restart(self, name, old, new): + if str(new) == "" or str(name) != "org.freedesktop.NetworkManager": + return + NMDbusInterface.last_disconnect = time.time() + time.sleep( + 1 + ) # Give NetworkManager a bit of time to start and rediscover itself. + for key in self.handlers: + val, self.handlers[key] = self.handlers[key], [] + for obj, func, args, kwargs in val: + try: + # This resets the object path if needed + obj.proxy + self.add_signal_receiver(key[0], key[1], obj, func, args, kwargs) + except ObjectVanished: + pass + + +SignalDispatcher = SignalDispatcher() + +# We completely dynamically generate all classes using introspection data. As +# this is done at import time, use a special dbus connection that does not get +# in the way of setting a mainloop and doing async stuff later. +try: + init_bus = dbus.SystemBus(private=True) +except dbus.exceptions.DBusException: + init_bus = None +xml_cache = {} + + +class NMDbusInterfaceType(type): + """Metaclass that generates our classes based on introspection data""" + + dbus_service = "org.freedesktop.NetworkManager" + + def __new__(type_, name, bases, attrs): + attrs["dbus_service"] = type_.dbus_service + attrs["properties"] = [] + attrs["introspection_data"] = None + attrs["signals"] = [] + + # Derive the interface name from the name of the class, but let classes + # override it if needed + if "interface_names" not in attrs and "NMDbusInterface" not in name: + attrs["interface_names"] = ["org.freedesktop.NetworkManager.%s" % name] + for base in bases: + if hasattr(base, "interface_names"): + attrs["interface_names"] = [ + "%s.%s" % (base.interface_names[0], name) + ] + base.interface_names + break + else: + for base in bases: + if hasattr(base, "interface_names"): + attrs["interface_names"] += base.interface_names + break + + if "interface_names" in attrs: + SignalDispatcher.interfaces.update(attrs["interface_names"]) + + # If we know where to find this object, let's introspect it and + # generate properties and methods + if "object_path" in attrs and attrs["object_path"]: + root = [] + if init_bus is not None: + proxy = init_bus.get_object(type_.dbus_service, attrs["object_path"]) + attrs["introspection_data"] = proxy.Introspect( + dbus_interface="org.freedesktop.DBus.Introspectable" + ) + root = etree.fromstring(attrs["introspection_data"]) + for element in root: + if ( + element.tag == "interface" + and element.attrib["name"] in attrs["interface_names"] + ): + for item in element: + if item.tag == "property": + attrs[item.attrib["name"]] = type_.make_property( + name, element.attrib["name"], item.attrib + ) + attrs["properties"].append(item.attrib["name"]) + elif item.tag == "method": + aname = item.attrib["name"] + if aname in attrs: + aname = "_" + aname + attrs[aname] = type_.make_method( + name, element.attrib["name"], item.attrib, list(item) + ) + elif item.tag == "signal": + SignalDispatcher.args[ + (element.attrib["name"], item.attrib["name"]) + ] = [ + (arg.attrib.get("name", None), arg.attrib["type"]) + for arg in item + ] + attrs["On" + item.attrib["name"]] = type_.make_signal( + name, element.attrib["name"], item.attrib + ) + attrs["signals"].append(item.attrib["name"]) + + klass = super(NMDbusInterfaceType, type_).__new__(type_, name, bases, attrs) + return klass + + @staticmethod + def make_property(klass, interface, attrib): + name = attrib["name"] + + def get_func(self): + try: + data = self.proxy.Get( + interface, name, dbus_interface="org.freedesktop.DBus.Properties" + ) + except dbus.exceptions.DBusException as e: + if e.get_dbus_name() == "org.freedesktop.DBus.Error.UnknownMethod": + raise ObjectVanished(self) + raise + return fixups.to_python(klass, "Get", name, data, attrib["type"]) + + if attrib["access"] == "read": + return property(get_func) + + def set_func(self, value): + value = fixups.to_dbus(klass, "Set", name, value, attrib["type"]) + try: + return self.proxy.Set( + interface, + name, + value, + dbus_interface="org.freedesktop.DBus.Properties", + ) + except dbus.exceptions.DBusException as e: + if e.get_dbus_name() == "org.freedesktop.DBus.Error.UnknownMethod": + raise ObjectVanished(self) + raise + + return property(get_func, set_func) + + @staticmethod + def make_method(klass, interface, attrib, args): + name = attrib["name"] + outargs = [x for x in args if x.tag == "arg" and x.attrib["direction"] == "out"] + outargstr = ", ".join([x.attrib["name"] for x in outargs]) or "ret" + args = [x for x in args if x.tag == "arg" and x.attrib["direction"] == "in"] + argstr = ", ".join([x.attrib["name"] for x in args]) + ret = {} + code = "def %s(self%s):\n" % (name, ", " + argstr if argstr else "") + for arg in args: + argname = arg.attrib["name"] + signature = arg.attrib["type"] + code += " %s = fixups.to_dbus('%s', '%s', '%s', %s, '%s')\n" % ( + argname, + klass, + name, + argname, + argname, + signature, + ) + code += " try:\n" + code += " %s = dbus.Interface(self.proxy, '%s').%s(%s)\n" % ( + outargstr, + interface, + name, + argstr, + ) + code += " except dbus.exceptions.DBusException as e:\n" + code += " if e.get_dbus_name() == 'org.freedesktop.DBus.Error.UnknownMethod':\n" + code += " raise ObjectVanished(self)\n" + code += " raise\n" + for arg in outargs: + argname = arg.attrib["name"] + signature = arg.attrib["type"] + code += " %s = fixups.to_python('%s', '%s', '%s', %s, '%s')\n" % ( + argname, + klass, + name, + argname, + argname, + signature, + ) + code += " return (%s)" % outargstr + exec(code, globals(), ret) + return ret[name] + + @staticmethod + def make_signal(klass, interface, attrib): + name = attrib["name"] + ret = {} + code = "def On%s(self, func, *args, **kwargs):" % name + code += ( + " SignalDispatcher.add_signal_receiver('%s', '%s', self, func, list(args), kwargs)" + % (interface, name) + ) + exec(code, globals(), ret) + return ret["On" + name] + + +@six.add_metaclass(NMDbusInterfaceType) +class NMDbusInterface(object): + object_path = None + last_disconnect = 0 + is_transient = False + + def __new__(klass, object_path=None): + # If we didn't introspect this one at definition time, let's do it now. + if object_path and not klass.introspection_data: + proxy = dbus.SystemBus().get_object(klass.dbus_service, object_path) + klass.introspection_data = proxy.Introspect( + dbus_interface="org.freedesktop.DBus.Introspectable" + ) + root = etree.fromstring(klass.introspection_data) + for element in root: + if ( + element.tag == "interface" + and element.attrib["name"] in klass.interface_names + ): + for item in element: + if item.tag == "property": + setattr( + klass, + item.attrib["name"], + type(klass).make_property( + klass.__name__, element.attrib["name"], item.attrib + ), + ) + klass.properties.append(item.attrib["name"]) + elif item.tag == "method": + aname = item.attrib["name"] + if hasattr(klass, aname): + aname = "_" + aname + setattr( + klass, + aname, + type(klass).make_method( + klass.__name__, + element.attrib["name"], + item.attrib, + list(item), + ), + ) + elif item.tag == "signal": + SignalDispatcher.args[ + (element.attrib["name"], item.attrib["name"]) + ] = [ + (arg.attrib.get("name", None), arg.attrib["type"]) + for arg in item + ] + setattr( + klass, + "On" + item.attrib["name"], + type(klass).make_signal( + klass.__name__, element.attrib["name"], item.attrib + ), + ) + klass.signals.append(item.attrib["name"]) + + SignalDispatcher.listen_for_restarts() + return super(NMDbusInterface, klass).__new__(klass) + + def __init__(self, object_path=None): + if isinstance(object_path, NMDbusInterface): + object_path = object_path.object_path + self.object_path = self.object_path or object_path + self._proxy = None + + def __eq__(self, other): + return ( + isinstance(other, NMDbusInterface) + and self.object_path + and other.object_path == self.object_path + ) + + @property + def proxy(self): + if not self._proxy: + self._proxy = dbus.SystemBus().get_object( + self.dbus_service, self.object_path, follow_name_owner_changes=True + ) + self._proxy.created = time.time() + elif self._proxy.created < self.last_disconnect: + if self.is_transient: + raise ObjectVanished(self) + obj = type(self)(self.object_path) + if obj.object_path != self.object_path: + self.object_path = obj.object_path + self._proxy = dbus.SystemBus().get_object( + self.dbus_service, self.object_path + ) + self._proxy.created = time.time() + return self._proxy + + # Backwards compatibility interface + def connect_to_signal(self, signal, handler, *args, **kwargs): + return getattr(self, "On" + signal)(handler, *args, **kwargs) + + +class TransientNMDbusInterface(NMDbusInterface): + is_transient = True + + +class NetworkManager(NMDbusInterface): + interface_names = ["org.freedesktop.NetworkManager"] + object_path = "/org/freedesktop/NetworkManager" + + # noop method for backward compatibility. It is no longer necessary to call + # this but let's not break code that does so. + def auto_reconnect(self): + pass + + +class Statistics(NMDbusInterface): + object_path = "/org/freedesktop/NetworkManager/Statistics" + + +class Settings(NMDbusInterface): + object_path = "/org/freedesktop/NetworkManager/Settings" + + +class AgentManager(NMDbusInterface): + object_path = "/org/freedesktop/NetworkManager/AgentManager" + + +class Connection(NMDbusInterface): + interface_names = ["org.freedesktop.NetworkManager.Settings.Connection"] + has_secrets = ["802-1x", "802-11-wireless-security", "cdma", "gsm", "pppoe", "vpn"] + + def __init__(self, object_path): + super(Connection, self).__init__(object_path) + self.uuid = self.GetSettings()["connection"]["uuid"] + + def GetSecrets(self, name=None): + settings = self.GetSettings() + if name is None: + name = settings["connection"]["type"] + name = settings[name].get("security", name) + try: + return self._GetSecrets(name) + except dbus.exceptions.DBusException as e: + if ( + e.get_dbus_name() + != "org.freedesktop.NetworkManager.AgentManager.NoSecrets" + ): + raise + return {key: {} for key in settings} + + @staticmethod + def all(): + return Settings.ListConnections() + + def __eq__(self, other): + return isinstance(other, type(self)) and self.uuid == other.uuid + + +class ActiveConnection(TransientNMDbusInterface): + interface_names = ["org.freedesktop.NetworkManager.Connection.Active"] + + def __new__(klass, object_path): + if klass == ActiveConnection: + # Automatically turn this into a VPNConnection if needed + obj = dbus.SystemBus().get_object(klass.dbus_service, object_path) + if obj.Get( + "org.freedesktop.NetworkManager.Connection.Active", + "Vpn", + dbus_interface="org.freedesktop.DBus.Properties", + ): + return VPNConnection.__new__(VPNConnection, object_path) + return super(ActiveConnection, klass).__new__(klass, object_path) + + def __eq__(self, other): + return isinstance(other, type(self)) and self.Uuid == other.Uuid + + +class VPNConnection(ActiveConnection): + interface_names = ["org.freedesktop.NetworkManager.VPN.Connection"] + + +class Device(NMDbusInterface): + interface_names = [ + "org.freedesktop.NetworkManager.Device", + "org.freedesktop.NetworkManager.Device.Statistics", + ] + + def __new__(klass, object_path): + if klass == Device: + # Automatically specialize the device + try: + obj = dbus.SystemBus().get_object(klass.dbus_service, object_path) + klass = device_class( + obj.Get( + "org.freedesktop.NetworkManager.Device", + "DeviceType", + dbus_interface="org.freedesktop.DBus.Properties", + ) + ) + return klass.__new__(klass, object_path) + except ObjectVanished: + pass + return super(Device, klass).__new__(klass, object_path) + + @staticmethod + def all(): + return NetworkManager.Devices + + def __eq__(self, other): + return isinstance(other, type(self)) and self.IpInterface == other.IpInterface + + # Backwards compatibility method. Devices now auto-specialize, so this is + # no longer needed. But code may use it. + def SpecificDevice(self): + return self + + +def device_class(typ): + return { + NM_DEVICE_TYPE_ADSL: Adsl, + NM_DEVICE_TYPE_BOND: Bond, + NM_DEVICE_TYPE_BRIDGE: Bridge, + NM_DEVICE_TYPE_BT: Bluetooth, + NM_DEVICE_TYPE_ETHERNET: Wired, + NM_DEVICE_TYPE_GENERIC: Generic, + NM_DEVICE_TYPE_INFINIBAND: Infiniband, + NM_DEVICE_TYPE_IP_TUNNEL: IPTunnel, + NM_DEVICE_TYPE_MACVLAN: Macvlan, + NM_DEVICE_TYPE_MODEM: Modem, + NM_DEVICE_TYPE_OLPC_MESH: OlpcMesh, + NM_DEVICE_TYPE_TEAM: Team, + NM_DEVICE_TYPE_TUN: Tun, + NM_DEVICE_TYPE_VETH: Veth, + NM_DEVICE_TYPE_VLAN: Vlan, + NM_DEVICE_TYPE_VXLAN: Vxlan, + NM_DEVICE_TYPE_WIFI: Wireless, + NM_DEVICE_TYPE_WIMAX: Wimax, + NM_DEVICE_TYPE_MACSEC: MacSec, + NM_DEVICE_TYPE_DUMMY: Dummy, + NM_DEVICE_TYPE_PPP: PPP, + NM_DEVICE_TYPE_OVS_INTERFACE: OvsIf, + NM_DEVICE_TYPE_OVS_PORT: OvsPort, + NM_DEVICE_TYPE_OVS_BRIDGE: OvsBridge, + NM_DEVICE_TYPE_WPAN: Wpan, + NM_DEVICE_TYPE_6LOWPAN: SixLoWpan, + NM_DEVICE_TYPE_WIREGUARD: WireGuard, + NM_DEVICE_TYPE_VRF: Vrf, + NM_DEVICE_TYPE_WIFI_P2P: WifiP2p, + NM_DEVICE_TYPE_LOOPBACK: Loopback, + }[typ] + + +class Adsl(Device): + pass + + +class Bluetooth(Device): + pass + + +class Bond(Device): + pass + + +class Bridge(Device): + pass + + +class Generic(Device): + pass + + +class Infiniband(Device): + pass + + +class IPTunnel(Device): + pass + + +class Macvlan(Device): + pass + + +class Modem(Device): + pass + + +class OlpcMesh(Device): + pass + + +class Team(Device): + pass + + +class Tun(Device): + pass + + +class Veth(Device): + pass + + +class Vlan(Device): + pass + + +class Vxlan(Device): + pass + + +class Wimax(Device): + pass + + +class Wired(Device): + pass + + +class Wireless(Device): + pass + + +class MacSec(Device): + pass + + +class Dummy(Device): + pass + + +class PPP(Device): + pass + + +class OvsIf(Device): + pass + + +class OvsPort(Device): + pass + + +class OvsBridge(Device): + pass + + +class Wpan(Device): + pass + + +class SixLoWpan(Device): + pass + + +class WireGuard(Device): + pass + + +class WifiP2p(Device): + pass + + +class Vrf(Device): + pass + + +class Loopback(Device): + pass + + +class NSP(TransientNMDbusInterface): + interface_names = ["org.freedesktop.NetworkManager.Wimax.NSP"] + + +class AccessPoint(NMDbusInterface): + @staticmethod + def all(): + for device in Device.all(): + if isinstance(device, Wireless): + for ap in device.AccessPoints: + yield ap + + def __eq__(self, other): + return isinstance(other, type(self)) and self.HwAddress == other.HwAddress + + +class IP4Config(TransientNMDbusInterface): + pass + + +class IP6Config(TransientNMDbusInterface): + pass + + +class DHCP4Config(TransientNMDbusInterface): + pass + + +class DHCP6Config(TransientNMDbusInterface): + pass + + +# Evil hack to work around not being able to specify a method name in the +# dbus.service.method decorator. +class SecretAgentType(type(dbus.service.Object)): + def __new__(type_, name, bases, attrs): + if bases != (dbus.service.Object,): + attrs["GetSecretsImpl"] = attrs.pop("GetSecrets") + return super(SecretAgentType, type_).__new__(type_, name, bases, attrs) + + +@six.add_metaclass(SecretAgentType) +class SecretAgent(dbus.service.Object): + object_path = "/org/freedesktop/NetworkManager/SecretAgent" + interface_name = "org.freedesktop.NetworkManager.SecretAgent" + + def __init__(self, identifier): + self.identifier = identifier + dbus.service.Object.__init__(self, dbus.SystemBus(), self.object_path) + AgentManager.Register(self.identifier) + + @dbus.service.method( + dbus_interface=interface_name, + in_signature="a{sa{sv}}osasu", + out_signature="a{sa{sv}}", + ) + def GetSecrets(self, connection, connection_path, setting_name, hints, flags): + settings = fixups.to_python( + "SecretAgent", "GetSecrets", "connection", connection, "a{sa{sv}}" + ) + connection = fixups.to_python( + "SecretAgent", "GetSecrets", "connection_path", connection_path, "o" + ) + setting_name = fixups.to_python( + "SecretAgent", "GetSecrets", "setting_name", setting_name, "s" + ) + hints = fixups.to_python("SecretAgent", "GetSecrets", "hints", hints, "as") + return self.GetSecretsImpl(settings, connection, setting_name, hints, flags) + + +# These two are interfaces that must be provided to NetworkManager. Keep them +# as comments for documentation purposes. +# +# class PPP(NMDbusInterface): pass +# class VPNPlugin(NMDbusInterface): +# interface_names = ['org.freedesktop.NetworkManager.VPN.Plugin'] + + +def const(prefix, val): + prefix = "NM_" + prefix.upper() + "_" + for key, vval in globals().items(): + if "REASON" in key and "REASON" not in prefix: + continue + if key.startswith(prefix) and val == vval: + return key.replace(prefix, "").lower() + raise ValueError("No constant found for %s* with value %d", (prefix, val)) + + +# Several fixer methods to make the data easier to handle in python +# - SSID sent/returned as bytes (only encoding tried is utf-8) +# - IP, Mac address and route metric encoding/decoding +class fixups(object): + @staticmethod + def to_dbus(klass, method, arg, val, signature): + if arg in ("connection" "properties") and signature == "a{sa{sv}}": + settings = copy.deepcopy(val) + for key in settings: + if "mac-address" in settings[key]: + settings[key]["mac-address"] = fixups.mac_to_dbus( + settings[key]["mac-address"] + ) + if "cloned-mac-address" in settings[key]: + settings[key]["cloned-mac-address"] = fixups.mac_to_dbus( + settings[key]["cloned-mac-address"] + ) + if "bssid" in settings[key]: + settings[key]["bssid"] = fixups.mac_to_dbus(settings[key]["bssid"]) + for cert in [ + "ca-cert", + "client-cert", + "phase2-ca-cert", + "phase2-client-cert", + "private-key", + ]: + if cert in settings[key]: + settings[key][cert] = fixups.cert_to_dbus(settings[key][cert]) + if "ssid" in settings.get("802-11-wireless", {}): + settings["802-11-wireless"]["ssid"] = fixups.ssid_to_dbus( + settings["802-11-wireless"]["ssid"] + ) + if "ipv4" in settings: + if "address-data" in settings["ipv4"]: + for item in settings["ipv4"]["address-data"]: + item["prefix"] = dbus.UInt32(item["prefix"]) + settings["ipv4"]["address-data"] = dbus.Array( + settings["ipv4"]["address-data"], + signature=dbus.Signature("a{sv}"), + ) + if "route-data" in settings["ipv4"]: + for item in settings["ipv4"]["route-data"]: + item["prefix"] = dbus.UInt32(item["prefix"]) + settings["ipv4"]["route-data"] = dbus.Array( + settings["ipv4"]["route-data"], + signature=dbus.Signature("a{sv}"), + ) + if "addresses" in settings["ipv4"]: + settings["ipv4"]["addresses"] = [ + fixups.addrconf_to_dbus(addr, socket.AF_INET) + for addr in settings["ipv4"]["addresses"] + ] + if "routes" in settings["ipv4"]: + settings["ipv4"]["routes"] = [ + fixups.route_to_dbus(route, socket.AF_INET) + for route in settings["ipv4"]["routes"] + ] + if "dns" in settings["ipv4"]: + settings["ipv4"]["dns"] = [ + fixups.addr_to_dbus(addr, socket.AF_INET) + for addr in settings["ipv4"]["dns"] + ] + if "ipv6" in settings: + if "address-data" in settings["ipv6"]: + for item in settings["ipv6"]["address-data"]: + item["prefix"] = dbus.UInt32(item["prefix"]) + settings["ipv6"]["address-data"] = dbus.Array( + settings["ipv6"]["address-data"], + signature=dbus.Signature("a{sv}"), + ) + if "route-data" in settings["ipv6"]: + for item in settings["ipv6"]["route-data"]: + item["prefix"] = dbus.UInt32(item["prefix"]) + settings["ipv6"]["route-data"] = dbus.Array( + settings["ipv6"]["route-data"], + signature=dbus.Signature("a{sv}"), + ) + if "addresses" in settings["ipv6"]: + settings["ipv6"]["addresses"] = [ + fixups.addrconf_to_dbus(addr, socket.AF_INET6) + for addr in settings["ipv6"]["addresses"] + ] + if "routes" in settings["ipv6"]: + settings["ipv6"]["routes"] = [ + fixups.route_to_dbus(route, socket.AF_INET6) + for route in settings["ipv6"]["routes"] + ] + if "dns" in settings["ipv6"]: + settings["ipv6"]["dns"] = [ + fixups.addr_to_dbus(addr, socket.AF_INET6) + for addr in settings["ipv6"]["dns"] + ] + # Get rid of empty arrays/dicts. dbus barfs on them (can't guess + # signatures), and if they were to get through, NetworkManager + # ignores them anyway. + for key in list(settings.keys()): + if isinstance(settings[key], dict): + for key2 in list(settings[key].keys()): + if settings[key][key2] in ({}, []): + del settings[key][key2] + if settings[key] in ({}, []): + del settings[key] + val = settings + return fixups.base_to_dbus(val) + + @staticmethod + def base_to_dbus(val): + if isinstance(val, NMDbusInterface): + return val.object_path + if hasattr(val.__class__, "mro"): + for klass in val.__class__.mro(): + if klass.__module__ in ("dbus", "_dbus_bindings"): + return val + if hasattr(val, "__iter__") and not isinstance(val, six.string_types): + if hasattr(val, "items"): + return dict([(x, fixups.base_to_dbus(y)) for x, y in val.items()]) + else: + return [fixups.base_to_dbus(x) for x in val] + return val + + @staticmethod + def to_python(klass, method, arg, val, signature): + val = fixups.base_to_python(val) + klass_af = {"IP4Config": socket.AF_INET, "IP6Config": socket.AF_INET6}.get( + klass, socket.AF_INET + ) + if method == "Get": + if arg == "Ip4Address": + return fixups.addr_to_python(val, socket.AF_INET) + if arg == "Ip6Address": + return fixups.addr_to_python(val, socket.AF_INET6) + if arg == "Ssid": + return fixups.ssid_to_python(val) + if arg == "Strength": + return fixups.strength_to_python(val) + if arg == "Addresses": + return [fixups.addrconf_to_python(addr, klass_af) for addr in val] + if arg == "Routes": + return [fixups.route_to_python(route, klass_af) for route in val] + if arg in ("Nameservers", "WinsServers"): + return [fixups.addr_to_python(addr, klass_af) for addr in val] + if arg == "Options": + for key in val: + if key.startswith("requested_"): + val[key] = bool(int(val[key])) + elif val[key].isdigit(): + val[key] = int(val[key]) + elif key in ("domain_name_servers", "ntp_servers", "routers"): + val[key] = val[key].split() + + return val + if method == "GetSettings": + if "ssid" in val.get("802-11-wireless", {}): + val["802-11-wireless"]["ssid"] = fixups.ssid_to_python( + val["802-11-wireless"]["ssid"] + ) + for key in val: + val_ = val[key] + if "mac-address" in val_: + val_["mac-address"] = fixups.mac_to_python(val_["mac-address"]) + if "cloned-mac-address" in val_: + val_["cloned-mac-address"] = fixups.mac_to_python( + val_["cloned-mac-address"] + ) + if "bssid" in val_: + val_["bssid"] = fixups.mac_to_python(val_["bssid"]) + if "ipv4" in val: + if "addresses" in val["ipv4"]: + val["ipv4"]["addresses"] = [ + fixups.addrconf_to_python(addr, socket.AF_INET) + for addr in val["ipv4"]["addresses"] + ] + if "routes" in val["ipv4"]: + val["ipv4"]["routes"] = [ + fixups.route_to_python(route, socket.AF_INET) + for route in val["ipv4"]["routes"] + ] + if "dns" in val["ipv4"]: + val["ipv4"]["dns"] = [ + fixups.addr_to_python(addr, socket.AF_INET) + for addr in val["ipv4"]["dns"] + ] + if "ipv6" in val: + if "addresses" in val["ipv6"]: + val["ipv6"]["addresses"] = [ + fixups.addrconf_to_python(addr, socket.AF_INET6) + for addr in val["ipv6"]["addresses"] + ] + if "routes" in val["ipv6"]: + val["ipv6"]["routes"] = [ + fixups.route_to_python(route, socket.AF_INET6) + for route in val["ipv6"]["routes"] + ] + if "dns" in val["ipv6"]: + val["ipv6"]["dns"] = [ + fixups.addr_to_python(addr, socket.AF_INET6) + for addr in val["ipv6"]["dns"] + ] + return val + if method == "PropertiesChanged": + for prop in val: + val[prop] = fixups.to_python(klass, "Get", prop, val[prop], None) + return val + + @staticmethod + def base_to_python(val): + if isinstance(val, dbus.ByteArray): + return "".join([str(x) for x in val]) + if isinstance(val, (dbus.Array, list, tuple)): + return [fixups.base_to_python(x) for x in val] + if isinstance(val, (dbus.Dictionary, dict)): + return dict( + [ + (fixups.base_to_python(x), fixups.base_to_python(y)) + for x, y in val.items() + ] + ) + if isinstance(val, dbus.ObjectPath): + for obj in (NetworkManager, Settings, AgentManager): + if val == obj.object_path: + return obj + if val.startswith("/org/freedesktop/NetworkManager/"): + classname = val.split("/")[4] + classname = { + "Settings": "Connection", + "Devices": "Device", + }.get(classname, classname) + return globals()[classname](val) + if val == "/": + return None + if isinstance(val, (dbus.Signature, dbus.String)): + return six.text_type(val) + if isinstance(val, dbus.Boolean): + return bool(val) + if isinstance( + val, + (dbus.Int16, dbus.UInt16, dbus.Int32, dbus.UInt32, dbus.Int64, dbus.UInt64), + ): + return int(val) + if isinstance(val, dbus.Byte): + return six.int2byte(int(val)) + return val + + @staticmethod + def ssid_to_python(ssid): + try: + return bytes().join(ssid).decode("utf-8") + except UnicodeDecodeError: + ssid = bytes().join(ssid).decode("utf-8", "replace") + warnings.warn("Unable to decode ssid %s properly" % ssid, UnicodeWarning) + return ssid + + @staticmethod + def ssid_to_dbus(ssid): + if isinstance(ssid, six.text_type): + ssid = ssid.encode("utf-8") + return [dbus.Byte(x) for x in ssid] + + @staticmethod + def strength_to_python(strength): + return struct.unpack("B", strength)[0] + + @staticmethod + def mac_to_python(mac): + return "%02X:%02X:%02X:%02X:%02X:%02X" % tuple([ord(x) for x in mac]) + + @staticmethod + def mac_to_dbus(mac): + return [dbus.Byte(int(x, 16)) for x in mac.split(":")] + + @staticmethod + def addrconf_to_python(addrconf, family): + addr, netmask, gateway = addrconf + return [ + fixups.addr_to_python(addr, family), + netmask, + fixups.addr_to_python(gateway, family), + ] + + @staticmethod + def addrconf_to_dbus(addrconf, family): + addr, netmask, gateway = addrconf + if family == socket.AF_INET: + return [ + fixups.addr_to_dbus(addr, family), + fixups.mask_to_dbus(netmask), + fixups.addr_to_dbus(gateway, family), + ] + else: + return dbus.Struct( + ( + fixups.addr_to_dbus(addr, family), + fixups.mask_to_dbus(netmask), + fixups.addr_to_dbus(gateway, family), + ), + signature="ayuay", + ) + + @staticmethod + def addr_to_python(addr, family): + if family == socket.AF_INET: + return socket.inet_ntop(family, struct.pack("I", addr)) + else: + return socket.inet_ntop(family, b"".join(addr)) + + @staticmethod + def addr_to_dbus(addr, family): + if family == socket.AF_INET: + return dbus.UInt32(struct.unpack("I", socket.inet_pton(family, addr))[0]) + else: + return dbus.ByteArray(socket.inet_pton(family, addr)) + + @staticmethod + def mask_to_dbus(mask): + return dbus.UInt32(mask) + + @staticmethod + def route_to_python(route, family): + addr, netmask, gateway, metric = route + return [ + fixups.addr_to_python(addr, family), + netmask, + fixups.addr_to_python(gateway, family), + metric, + ] + + @staticmethod + def route_to_dbus(route, family): + addr, netmask, gateway, metric = route + return [ + fixups.addr_to_dbus(addr, family), + fixups.mask_to_dbus(netmask), + fixups.addr_to_dbus(gateway, family), + metric, + ] + + @staticmethod + def cert_to_dbus(cert): + if not isinstance(cert, bytes): + if not cert.startswith("file://"): + cert = "file://" + cert + cert = cert.encode("utf-8") + b"\0" + return [dbus.Byte(x) for x in cert] + + +# Turn NetworkManager and Settings into singleton objects +NetworkManager = NetworkManager() +Settings = Settings() +AgentManager = AgentManager() +if init_bus is not None: + init_bus.close() +del init_bus +del xml_cache + +# Constants below are generated with makeconstants.py. Do not edit manually. +NM_CAPABILITY_TEAM = 1 +NM_CAPABILITY_OVS = 2 +NM_STATE_UNKNOWN = 0 +NM_STATE_ASLEEP = 10 +NM_STATE_DISCONNECTED = 20 +NM_STATE_DISCONNECTING = 30 +NM_STATE_CONNECTING = 40 +NM_STATE_CONNECTED_LOCAL = 50 +NM_STATE_CONNECTED_SITE = 60 +NM_STATE_CONNECTED_GLOBAL = 70 +NM_CONNECTIVITY_UNKNOWN = 0 +NM_CONNECTIVITY_NONE = 1 +NM_CONNECTIVITY_PORTAL = 2 +NM_CONNECTIVITY_LIMITED = 3 +NM_CONNECTIVITY_FULL = 4 +NM_DEVICE_TYPE_UNKNOWN = 0 +NM_DEVICE_TYPE_ETHERNET = 1 +NM_DEVICE_TYPE_WIFI = 2 +NM_DEVICE_TYPE_UNUSED1 = 3 +NM_DEVICE_TYPE_UNUSED2 = 4 +NM_DEVICE_TYPE_BT = 5 +NM_DEVICE_TYPE_OLPC_MESH = 6 +NM_DEVICE_TYPE_WIMAX = 7 +NM_DEVICE_TYPE_MODEM = 8 +NM_DEVICE_TYPE_INFINIBAND = 9 +NM_DEVICE_TYPE_BOND = 10 +NM_DEVICE_TYPE_VLAN = 11 +NM_DEVICE_TYPE_ADSL = 12 +NM_DEVICE_TYPE_BRIDGE = 13 +NM_DEVICE_TYPE_GENERIC = 14 +NM_DEVICE_TYPE_TEAM = 15 +NM_DEVICE_TYPE_TUN = 16 +NM_DEVICE_TYPE_IP_TUNNEL = 17 +NM_DEVICE_TYPE_MACVLAN = 18 +NM_DEVICE_TYPE_VXLAN = 19 +NM_DEVICE_TYPE_VETH = 20 +NM_DEVICE_TYPE_MACSEC = 21 +NM_DEVICE_TYPE_DUMMY = 22 +NM_DEVICE_TYPE_PPP = 23 +NM_DEVICE_TYPE_OVS_INTERFACE = 24 +NM_DEVICE_TYPE_OVS_PORT = 25 +NM_DEVICE_TYPE_OVS_BRIDGE = 26 +NM_DEVICE_TYPE_WPAN = 27 +NM_DEVICE_TYPE_6LOWPAN = 28 +NM_DEVICE_TYPE_WIREGUARD = 29 +NM_DEVICE_TYPE_WIFI_P2P = 30 +NM_DEVICE_TYPE_VRF = 31 +NM_DEVICE_TYPE_LOOPBACK = 32 +NM_DEVICE_CAP_NONE = 0 +NM_DEVICE_CAP_NM_SUPPORTED = 1 +NM_DEVICE_CAP_CARRIER_DETECT = 2 +NM_DEVICE_CAP_IS_SOFTWARE = 4 +NM_DEVICE_CAP_SRIOV = 8 +NM_WIFI_DEVICE_CAP_NONE = 0 +NM_WIFI_DEVICE_CAP_CIPHER_WEP40 = 1 +NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 2 +NM_WIFI_DEVICE_CAP_CIPHER_TKIP = 4 +NM_WIFI_DEVICE_CAP_CIPHER_CCMP = 8 +NM_WIFI_DEVICE_CAP_WPA = 16 +NM_WIFI_DEVICE_CAP_RSN = 32 +NM_WIFI_DEVICE_CAP_AP = 64 +NM_WIFI_DEVICE_CAP_ADHOC = 128 +NM_WIFI_DEVICE_CAP_FREQ_VALID = 256 +NM_WIFI_DEVICE_CAP_FREQ_2GHZ = 512 +NM_WIFI_DEVICE_CAP_FREQ_5GHZ = 1024 +NM_WIFI_DEVICE_CAP_MESH = 4096 +NM_WIFI_DEVICE_CAP_IBSS_RSN = 8192 +NM_802_11_AP_FLAGS_NONE = 0 +NM_802_11_AP_FLAGS_PRIVACY = 1 +NM_802_11_AP_FLAGS_WPS = 2 +NM_802_11_AP_FLAGS_WPS_PBC = 4 +NM_802_11_AP_FLAGS_WPS_PIN = 8 +NM_802_11_AP_SEC_NONE = 0 +NM_802_11_AP_SEC_PAIR_WEP40 = 1 +NM_802_11_AP_SEC_PAIR_WEP104 = 2 +NM_802_11_AP_SEC_PAIR_TKIP = 4 +NM_802_11_AP_SEC_PAIR_CCMP = 8 +NM_802_11_AP_SEC_GROUP_WEP40 = 16 +NM_802_11_AP_SEC_GROUP_WEP104 = 32 +NM_802_11_AP_SEC_GROUP_TKIP = 64 +NM_802_11_AP_SEC_GROUP_CCMP = 128 +NM_802_11_AP_SEC_KEY_MGMT_PSK = 256 +NM_802_11_AP_SEC_KEY_MGMT_802_1X = 512 +NM_802_11_AP_SEC_KEY_MGMT_SAE = 1024 +NM_802_11_AP_SEC_KEY_MGMT_OWE = 2048 +NM_802_11_AP_SEC_KEY_MGMT_OWE_TM = 4096 +NM_802_11_MODE_UNKNOWN = 0 +NM_802_11_MODE_ADHOC = 1 +NM_802_11_MODE_INFRA = 2 +NM_802_11_MODE_AP = 3 +NM_802_11_MODE_MESH = 4 +NM_BT_CAPABILITY_NONE = 0 +NM_BT_CAPABILITY_DUN = 1 +NM_BT_CAPABILITY_NAP = 2 +NM_DEVICE_MODEM_CAPABILITY_NONE = 0 +NM_DEVICE_MODEM_CAPABILITY_POTS = 1 +NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO = 2 +NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS = 4 +NM_DEVICE_MODEM_CAPABILITY_LTE = 8 +NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN = 0 +NM_WIMAX_NSP_NETWORK_TYPE_HOME = 1 +NM_WIMAX_NSP_NETWORK_TYPE_PARTNER = 2 +NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER = 3 +NM_DEVICE_STATE_UNKNOWN = 0 +NM_DEVICE_STATE_UNMANAGED = 10 +NM_DEVICE_STATE_UNAVAILABLE = 20 +NM_DEVICE_STATE_DISCONNECTED = 30 +NM_DEVICE_STATE_PREPARE = 40 +NM_DEVICE_STATE_CONFIG = 50 +NM_DEVICE_STATE_NEED_AUTH = 60 +NM_DEVICE_STATE_IP_CONFIG = 70 +NM_DEVICE_STATE_IP_CHECK = 80 +NM_DEVICE_STATE_SECONDARIES = 90 +NM_DEVICE_STATE_ACTIVATED = 100 +NM_DEVICE_STATE_DEACTIVATING = 110 +NM_DEVICE_STATE_FAILED = 120 +NM_DEVICE_STATE_REASON_NONE = 0 +NM_DEVICE_STATE_REASON_UNKNOWN = 1 +NM_DEVICE_STATE_REASON_NOW_MANAGED = 2 +NM_DEVICE_STATE_REASON_NOW_UNMANAGED = 3 +NM_DEVICE_STATE_REASON_CONFIG_FAILED = 4 +NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE = 5 +NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED = 6 +NM_DEVICE_STATE_REASON_NO_SECRETS = 7 +NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 +NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED = 9 +NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED = 10 +NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT = 11 +NM_DEVICE_STATE_REASON_PPP_START_FAILED = 12 +NM_DEVICE_STATE_REASON_PPP_DISCONNECT = 13 +NM_DEVICE_STATE_REASON_PPP_FAILED = 14 +NM_DEVICE_STATE_REASON_DHCP_START_FAILED = 15 +NM_DEVICE_STATE_REASON_DHCP_ERROR = 16 +NM_DEVICE_STATE_REASON_DHCP_FAILED = 17 +NM_DEVICE_STATE_REASON_SHARED_START_FAILED = 18 +NM_DEVICE_STATE_REASON_SHARED_FAILED = 19 +NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED = 20 +NM_DEVICE_STATE_REASON_AUTOIP_ERROR = 21 +NM_DEVICE_STATE_REASON_AUTOIP_FAILED = 22 +NM_DEVICE_STATE_REASON_MODEM_BUSY = 23 +NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE = 24 +NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER = 25 +NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT = 26 +NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED = 27 +NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED = 28 +NM_DEVICE_STATE_REASON_GSM_APN_FAILED = 29 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING = 30 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED = 31 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT = 32 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED = 33 +NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED = 34 +NM_DEVICE_STATE_REASON_FIRMWARE_MISSING = 35 +NM_DEVICE_STATE_REASON_REMOVED = 36 +NM_DEVICE_STATE_REASON_SLEEPING = 37 +NM_DEVICE_STATE_REASON_CONNECTION_REMOVED = 38 +NM_DEVICE_STATE_REASON_USER_REQUESTED = 39 +NM_DEVICE_STATE_REASON_CARRIER = 40 +NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED = 41 +NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE = 42 +NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND = 43 +NM_DEVICE_STATE_REASON_BT_FAILED = 44 +NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED = 45 +NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED = 46 +NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED = 47 +NM_DEVICE_STATE_REASON_GSM_SIM_WRONG = 48 +NM_DEVICE_STATE_REASON_INFINIBAND_MODE = 49 +NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED = 50 +NM_DEVICE_STATE_REASON_BR2684_FAILED = 51 +NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE = 52 +NM_DEVICE_STATE_REASON_SSID_NOT_FOUND = 53 +NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED = 54 +NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED = 55 +NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED = 56 +NM_DEVICE_STATE_REASON_MODEM_FAILED = 57 +NM_DEVICE_STATE_REASON_MODEM_AVAILABLE = 58 +NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT = 59 +NM_DEVICE_STATE_REASON_NEW_ACTIVATION = 60 +NM_DEVICE_STATE_REASON_PARENT_CHANGED = 61 +NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62 +NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63 +NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE = 64 +NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED = 65 +NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED = 66 +NM_DEVICE_STATE_REASON_PEER_NOT_FOUND = 67 +NM_METERED_UNKNOWN = 0 +NM_METERED_YES = 1 +NM_METERED_NO = 2 +NM_METERED_GUESS_YES = 3 +NM_METERED_GUESS_NO = 4 +NM_CONNECTION_MULTI_CONNECT_DEFAULT = 0 +NM_CONNECTION_MULTI_CONNECT_SINGLE = 1 +NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE = 2 +NM_CONNECTION_MULTI_CONNECT_MULTIPLE = 3 +NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 +NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 +NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 +NM_ACTIVE_CONNECTION_STATE_DEACTIVATING = 3 +NM_ACTIVE_CONNECTION_STATE_DEACTIVATED = 4 +NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN = 0 +NM_ACTIVE_CONNECTION_STATE_REASON_NONE = 1 +NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED = 2 +NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED = 3 +NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED = 4 +NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID = 5 +NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT = 6 +NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT = 7 +NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED = 8 +NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS = 9 +NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED = 10 +NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED = 11 +NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED = 12 +NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED = 13 +NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED = 14 +NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0 +NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 1 +NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 2 +NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 4 +NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE = 8 +NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM = 2147483648 +NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS = 1073741824 +NM_IP_TUNNEL_MODE_UNKNOWN = 0 +NM_IP_TUNNEL_MODE_IPIP = 1 +NM_IP_TUNNEL_MODE_GRE = 2 +NM_IP_TUNNEL_MODE_SIT = 3 +NM_IP_TUNNEL_MODE_ISATAP = 4 +NM_IP_TUNNEL_MODE_VTI = 5 +NM_IP_TUNNEL_MODE_IP6IP6 = 6 +NM_IP_TUNNEL_MODE_IPIP6 = 7 +NM_IP_TUNNEL_MODE_IP6GRE = 8 +NM_IP_TUNNEL_MODE_VTI6 = 9 +NM_IP_TUNNEL_MODE_GRETAP = 10 +NM_IP_TUNNEL_MODE_IP6GRETAP = 11 +NM_CHECKPOINT_CREATE_FLAG_NONE = 0 +NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 1 +NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 2 +NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 4 +NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING = 8 +NM_ROLLBACK_RESULT_OK = 0 +NM_ROLLBACK_RESULT_ERR_NO_DEVICE = 1 +NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED = 2 +NM_ROLLBACK_RESULT_ERR_FAILED = 3 +NM_SETTINGS_CONNECTION_FLAG_NONE = 0 +NM_SETTINGS_CONNECTION_FLAG_UNSAVED = 1 +NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED = 2 +NM_SETTINGS_CONNECTION_FLAG_VOLATILE = 4 +NM_SETTINGS_CONNECTION_FLAG_EXTERNAL = 8 +NM_ACTIVATION_STATE_FLAG_NONE = 0 +NM_ACTIVATION_STATE_FLAG_IS_MASTER = 1 +NM_ACTIVATION_STATE_FLAG_IS_SLAVE = 2 +NM_ACTIVATION_STATE_FLAG_LAYER2_READY = 4 +NM_ACTIVATION_STATE_FLAG_IP4_READY = 8 +NM_ACTIVATION_STATE_FLAG_IP6_READY = 16 +NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = 32 +NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = 64 +NM_ACTIVATION_STATE_FLAG_EXTERNAL = 128 +NM_SETTINGS_ADD_CONNECTION2_FLAG_NONE = 0 +NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK = 1 +NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY = 2 +NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT = 32 +NM_SETTINGS_UPDATE2_FLAG_NONE = 0 +NM_SETTINGS_UPDATE2_FLAG_TO_DISK = 1 +NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY = 2 +NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED = 4 +NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY = 8 +NM_SETTINGS_UPDATE2_FLAG_VOLATILE = 16 +NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = 32 +NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY = 64 +NM_TERNARY_DEFAULT = -1 +NM_TERNARY_FALSE = 0 +NM_TERNARY_TRUE = 1 +NM_MANAGER_RELOAD_FLAG_NONE = 0 +NM_MANAGER_RELOAD_FLAG_CONF = 1 +NM_MANAGER_RELOAD_FLAG_DNS_RC = 2 +NM_MANAGER_RELOAD_FLAG_DNS_FULL = 4 +NM_MANAGER_RELOAD_FLAG_ALL = 7 +NM_DEVICE_INTERFACE_FLAG_NONE = 0 +NM_DEVICE_INTERFACE_FLAG_UP = 1 +NM_DEVICE_INTERFACE_FLAG_LOWER_UP = 2 +NM_DEVICE_INTERFACE_FLAG_CARRIER = 65536 +NM_CLIENT_PERMISSION_NONE = 0 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK = 1 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI = 2 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN = 3 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX = 4 +NM_CLIENT_PERMISSION_SLEEP_WAKE = 5 +NM_CLIENT_PERMISSION_NETWORK_CONTROL = 6 +NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED = 7 +NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN = 8 +NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM = 9 +NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN = 10 +NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME = 11 +NM_CLIENT_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS = 12 +NM_CLIENT_PERMISSION_RELOAD = 13 +NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK = 14 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS = 15 +NM_CLIENT_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK = 16 +NM_CLIENT_PERMISSION_WIFI_SCAN = 17 +NM_CLIENT_PERMISSION_LAST = 17 +NM_CLIENT_PERMISSION_RESULT_UNKNOWN = 0 +NM_CLIENT_PERMISSION_RESULT_YES = 1 +NM_CLIENT_PERMISSION_RESULT_AUTH = 2 +NM_CLIENT_PERMISSION_RESULT_NO = 3 +NM_VPN_SERVICE_STATE_UNKNOWN = 0 +NM_VPN_SERVICE_STATE_INIT = 1 +NM_VPN_SERVICE_STATE_SHUTDOWN = 2 +NM_VPN_SERVICE_STATE_STARTING = 3 +NM_VPN_SERVICE_STATE_STARTED = 4 +NM_VPN_SERVICE_STATE_STOPPING = 5 +NM_VPN_SERVICE_STATE_STOPPED = 6 +NM_VPN_CONNECTION_STATE_UNKNOWN = 0 +NM_VPN_CONNECTION_STATE_PREPARE = 1 +NM_VPN_CONNECTION_STATE_NEED_AUTH = 2 +NM_VPN_CONNECTION_STATE_CONNECT = 3 +NM_VPN_CONNECTION_STATE_IP_CONFIG_GET = 4 +NM_VPN_CONNECTION_STATE_ACTIVATED = 5 +NM_VPN_CONNECTION_STATE_FAILED = 6 +NM_VPN_CONNECTION_STATE_DISCONNECTED = 7 +NM_VPN_CONNECTION_STATE_REASON_UNKNOWN = 0 +NM_VPN_CONNECTION_STATE_REASON_NONE = 1 +NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED = 2 +NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED = 3 +NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED = 4 +NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID = 5 +NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT = 6 +NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT = 7 +NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED = 8 +NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS = 9 +NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED = 10 +NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED = 11 +NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED = 0 +NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED = 1 +NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG = 2 +NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED = 0 +NM_SECRET_AGENT_ERROR_INVALID_CONNECTION = 1 +NM_SECRET_AGENT_ERROR_USER_CANCELED = 2 +NM_SECRET_AGENT_ERROR_AGENT_CANCELED = 3 +NM_SECRET_AGENT_ERROR_INTERNAL_ERROR = 4 +NM_SECRET_AGENT_ERROR_NO_SECRETS = 5 diff --git a/stage3-wifi/00-install/files/comitup-callback.sh b/stage3-wifi/00-install/files/comitup-callback.sh old mode 100644 new mode 100755 index 30e947b..d47f90b --- a/stage3-wifi/00-install/files/comitup-callback.sh +++ b/stage3-wifi/00-install/files/comitup-callback.sh @@ -17,6 +17,6 @@ # # FIXME: https://github.com/snstac/aryaos/issues/48 -logger "comitup callback: $*" +logger "AryaOS comitup callback: $*" # Ping a callback on Node-RED: wget -a http://127.0.0.1:1880/comitup_callback/$* diff --git a/stage3-wifi/00-install/files/python-networkmanager-main.zip b/stage3-wifi/00-install/files/python-networkmanager-main.zip deleted file mode 100644 index 5d9710a..0000000 Binary files a/stage3-wifi/00-install/files/python-networkmanager-main.zip and /dev/null differ diff --git a/stage3-wifi/00-install/files/run_comitup.sh b/stage3-wifi/00-install/files/run_comitup.sh index 96af515..6c765d0 100755 --- a/stage3-wifi/00-install/files/run_comitup.sh +++ b/stage3-wifi/00-install/files/run_comitup.sh @@ -17,32 +17,33 @@ # -AOS_CONFIG="/boot/${AOS_FLAVOR}-config.txt" +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" COMITUP_CONF="/boot/comitup.conf" -if [[ -f ${AOS_CONFIG} ]]; then - . ${AOS_CONFIG} +if [ -f $AOS_CONFIG ]; then + . $AOS_CONFIG fi -if [[ ! -f ${COMITUP_CONF} ]]; then - echo "${COMITUP_CONF} doesn't exist, initializing." - echo "# ap_name:" >> ${COMITUP_CONF} +if [ ! -f $COMITUP_CONF ]; then + echo "$COMITUP_CONF doesn't exist, initializing." + echo "# ap_name:" >> $COMITUP_CONF fi -if ! grep -qs -e 'ap_name' ${COMITUP_CONF}; then - echo "Adding empty ap_name to ${COMITUP_CONF}" - echo "# ap_name:" >> ${COMITUP_CONF} +if ! grep -qs -e 'ap_name' $COMITUP_CONF; then + echo "Adding empty ap_name to $COMITUP_CONF" + echo "# ap_name:" >> $COMITUP_CONF fi -if [ -z "$NODE_ID" ]; then - echo "NODE_ID not set, will retry..." - exit 1 +if [ -z "$WIFI_SSID" ]; then + if [ -z "$NODE_ID" ]; then + echo "NODE_ID not set, will retry..." + exit 1 + fi + WIFI_SSID="${AOS_FLAVOR:-AryaOS}-${NODE_ID: -4}" + logger "AryaOS comitup generated new WIFI_SSID: $WIFI_SSID" fi - -NEW_SSID="${AT_FLAVOR_ST}-${NODE_ID: -4}" -sed --follow-symlinks -i -E -e "s/# ap_name:.*/ap_name: $NEW_SSID/" ${COMITUP_CONF} -echo "comitup SSID is: $NEW_SSID" -logger "comitup SSID: $NEW_SSID" +sed --follow-symlinks -i -E -e "s/# ap_name:.*/ap_name: $WIFI_SSID/" $COMITUP_CONF +logger "AryaOS comitup using SSID: $WIFI_SSID" /usr/sbin/comitup diff --git a/stage4-node-red/00-install/files/update-nodejs-and-nodered b/stage4-node-red/00-install/files/update-nodejs-and-nodered index c08cb6a..6215410 100644 --- a/stage4-node-red/00-install/files/update-nodejs-and-nodered +++ b/stage4-node-red/00-install/files/update-nodejs-and-nodered @@ -1,6 +1,8 @@ #!/bin/bash +# AryaOS update-nodejs-and-nodered +# Source: https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered # -# Copyright 2016,2023 JS Foundation and other contributors, https://js.foundation/ +# Copyright 2016,2024 JS Foundation and other contributors, https://js.foundation/ # Copyright 2015,2016 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +28,7 @@ tgta16=16.20.2 # need armv6l latest from https://unofficial-builds.nodejs.org/ tgtl16=16.20.2 # need x86 latest from https://unofficial-builds.nodejs.org/download/release/ tgta18=18.19.0 # need armv6l latest from https://unofficial-builds.nodejs.org/download/release/ tgtl18=18.4.0 # need x86 latest from https://unofficial-builds.nodejs.org/download/release/ -tgta20=20.10.0 # need armv6l latest from https://unofficial-builds.nodejs.org/download/release/ +tgta20=20.11.0 # need armv6l latest from https://unofficial-builds.nodejs.org/download/release/ # tgtl20=None # need x86 latest from https://unofficial-builds.nodejs.org/download/release/ usage() { @@ -195,6 +197,12 @@ if [ "$EUID" == "0" ]; then SUDO='' SUDOE='' id -u nobody &>/dev/null || adduser --no-create-home --shell /dev/null --disabled-password --disabled-login --gecos '' nobody &>/dev/null +else + groups "$USER" | grep -q '\bsudo\b' && GRS="Y" || GRS="N" + if [[ "$GRS" == "N" ]]; then + echo "User $NODERED_USER not in sudoers group. Exiting" + exit 1; + fi fi # setup user, home and group @@ -205,7 +213,11 @@ if [[ "$NODERED_USER" == "" ]]; then else NODERED_GROUP="$NODERED_USER" NODERED_HOME="/home/$NODERED_USER" + if [[ "$NODERED_USER" == "root" ]]; then + NODERED_HOME="/root" + fi fi +SUDOU="sudo -u $NODERED_USER" if [[ "$(uname)" != "Darwin" ]]; then if curl -I https://registry.npmjs.org/@node-red/util >/dev/null 2>&1; then @@ -260,7 +272,8 @@ case $yn in echo -e "\nRunning Node-RED $EXTRAW for user $NODERED_USER at $NODERED_HOME on $MYOS\n" nv=0 - nv2="" + # nv2="" + nv2=`dpkg -s nodejs 2>/dev/null | grep Version | cut -d ' ' -f 2` nrv=`echo $NODERED_VERSION | cut -d "." -f1` if [[ "$APTOK" == "false" ]]; then @@ -279,10 +292,10 @@ case $yn in if [[ "$APTOK" == "true" ]]; then ndeb=$(apt-cache policy nodejs | grep Installed | awk '{print $2}') fi - if HAS_NODE; then + if HAS_NODE && HAS_NPM; then nv=`node -v | cut -d "." -f1 | cut -d "v" -f2` nvs=`node -v | cut -d "." -f2` - nv2=`node -v` + # nv2=`node -v` # nv2=`apt list nodejs 2>/dev/null | grep dfsg | cut -d ' ' -f 2 | cut -d '-' -f 1` echo "Already have nodejs $nv2" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi @@ -305,7 +318,7 @@ case $yn in # echo " " # echo " You can use the old v1 branch by specifying --nodered-version=1.*" echo " " - echo " You can force an install of node 16, 18 or 20 by adding --node16, --node18 or --node20 to the end of the command line above." + echo " You can force an install of node 16, 18 or 20 by adding --node18 or --node20 to the end of the command line above." echo " However doing so may break some nodes that may need re-installing manually." echo " Generally it is recommended to update all nodes to their latest versions before upgrading." echo " " @@ -318,7 +331,7 @@ case $yn in fi echo " Please backup your installation and flows before upgrading." echo " " - if ! $SUDO grep -q BCM2 /proc/cpuinfo; then + if ! $SUDO grep -q Raspberry /proc/cpuinfo; then echo " Note: not all embedded hardware can be updated via this method - please check before proceeding." echo " " fi @@ -373,7 +386,7 @@ case $yn in echo -ne " Leave existing Node.js :" elif [[ "$NODE_VERSION" == "" && "$nv" -ne 0 ]]; then CHAR="-" - echo -ne " Node option not specified : --node16, --node18 or --node20\n" + echo -ne " Node option not specified : --node18 or --node20\n" echo -ne " Leave existing Node.js :" else if [[ "$NODE_VERSION" == "12" ]]; then @@ -484,8 +497,13 @@ case $yn in echo -ne " Remove old version of Node.js \033[1;32m\u2714\033[0m $nv2\r\n" echo "Grab the LTS bundle" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null echo -ne " Install Node.js $NODE_VERSION LTS \r" - # use the official script to install for other debian platforms + # block debian nodejs install + echo "Package: nodejs" | $SUDO tee /etc/apt/preferences.d/nodejs.pref >>/dev/null + echo "Pin: release a=stable-security" | $SUDO tee -a /etc/apt/preferences.d/nodejs.pref >>/dev/null + echo "Pin-Priority: -1" | $SUDO tee -a /etc/apt/preferences.d/nodejs.pref >>/dev/null + + # use the official script to install for other debian platforms $SUDO apt install -y ca-certificates curl gnupg 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO mkdir -p /etc/apt/keyrings 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | $SUDOE gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null @@ -493,7 +511,9 @@ case $yn in echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" | $SUDOE tee -a /etc/apt/sources.list.d/nodesource.list >>/dev/null $SUDO apt-get update 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if $SUDO apt install -y nodejs 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null; then CHAR=$TICK; else CHAR=$CROSS; fi - echo -ne " Install Node.js $NODE_VERSION LTS $CHAR" + nov2=$(dpkg -s nodejs | grep Version | cut -d ' ' -f 2) + echo -ne " Install Node $nov2 $CHAR" + # echo -ne " Install Node.js $NODE_VERSION LTS $CHAR" fi fi @@ -667,12 +687,12 @@ case $yn in # remove unneeded large sentiment library to save space and load time $SUDO rm -f /usr/lib/node_modules/node-red/node_modules/multilang-sentiment/build/output/build-all.json 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null # on LXDE add launcher to top bar, refresh desktop menu - file=/home/$NODERED_USER/.config/lxpanel/LXDE-pi/panels/panel - if [ -e $file ]; then + pfile=/home/$NODERED_USER/.config/lxpanel/LXDE-pi/panels/panel + if [ -e $pfile ]; then if ! grep -q "Node-RED" $file; then mat="lxterminal.desktop" ins="lxterminal.desktop\n }\n Button {\n id=Node-RED.desktop" - $SUDO sed -i "s|$mat|$ins|" $file 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + $SUDO sed -i "s|$mat|$ins|" $pfile 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null if xhost >& /dev/null ; then export DISPLAY=:0 && lxpanelctl restart 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null fi @@ -681,10 +701,22 @@ case $yn in # on Pi, add launcher to top bar, add cpu temp example, make sure ping works echo "Now add launcher to top bar, add cpu temp example, make sure ping works" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null - if $SUDO grep -q BCM2 /proc/cpuinfo; then + if $SUDO grep -q Raspberry /proc/cpuinfo; then # $SUDO setcap cap_net_raw+eip $(eval readlink -f `which node`) 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null $SUDO adduser $NODERED_USER gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null - $SUDO apt install -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + FAM=$(cat /etc/issue | cut -d ' ' -f 1) + ISS=$(cat /etc/issue | cut -d '/' -f 2 | cut -d ' ' -f 2) + if [[ "$FAM" == *"bian" ]]; then + if (($ISS > 11)); then + echo "Replace old rpi.gpio with lgpio" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + $SUDO apt purge -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + $SUDO apt install -y python3-pip 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + $SUDO pip3 install --break-system-packages rpi-lgpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + else + echo "Leaving old rpi.gpio" | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + $SUDO apt install -y python3-rpi.gpio 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null + fi + fi fi $SUDO setcap cap_net_raw=ep /bin/ping 2>&1 | $SUDO tee -a /var/log/nodered-install.log >>/dev/null @@ -723,6 +755,9 @@ case $yn in echo "Finished: $(date)" | $SUDO tee -a /var/log/nodered-install.log file=/home/$NODERED_USER/.node-red/settings.js + if [[ "$NODERED_USER" == "root" ]]; then + file=/root/.node-red/settings.js + fi if [ ! -f $file ]; then echo " " elif ! diff -q /usr/lib/node_modules/node-red/settings.js $file &>/dev/null 2>&1 ; then @@ -752,7 +787,7 @@ case $yn in echo " sudo rm -f /etc/sudoers.d/010_pi-nopasswd" echo " " fi - if [ ! -f ~/.node-red/settings.js ]; then + if [ ! -f $file ]; then echo " - You can customise the initial settings by running:" echo " " echo " node-red admin init" @@ -774,21 +809,26 @@ case $yn in fi echo "**********************************************************************************" echo " " - if [ ! -f ~/.node-red/settings.js ]; then + if [ ! -f $file ]; then initset="${INITSET}" - [ ! "${INITSET}" ] && read -t 60 -p " Would you like to customise the settings now (y/N) ? " initset + # [ ! "${INITSET}" ] && read -t 60 -p " Would you like to customise the settings now (y/N) ? " initset case $initset in [Yy]* ) - node-red admin init - $SUDO chown 0:0 ~/.node-red/settings.js + export HOSTIP=`hostname -I | cut -d ' ' -f 1` + $SUDO chown -Rf $NODERED_USER:$NODERED_GROUP $NODERED_HOME/.node-red 2>&1 >>/dev/null + $SUDOU /usr/bin/node-red admin init + $SUDO chown 0:0 $file ;; - "n") + [Nn]* ) echo "Settings not initialized." exit 0 ;; * ) - echo " " - exit 1 + # echo " " + # exit 1 + $SUDO chown -Rf $NODERED_USER:$NODERED_GROUP $NODERED_HOME/.node-red/ 2>&1 >>/dev/null + $SUDOU /usr/bin/node-red admin init + $SUDO chown 0:0 $file ;; esac fi diff --git a/stage5-common/00-install/00-packages b/stage5-common/00-install/00-packages deleted file mode 100644 index f6fcdb8..0000000 --- a/stage5-common/00-install/00-packages +++ /dev/null @@ -1,9 +0,0 @@ -gpsd -gpsd-clients - -python3-pip - -# Needy developer: -git -tmux -vim diff --git a/stage5-common/00-install/00-run.sh b/stage5-common/00-install/00-run.sh index d5da875..15ed12a 100755 --- a/stage5-common/00-install/00-run.sh +++ b/stage5-common/00-install/00-run.sh @@ -14,35 +14,40 @@ # limitations under the License. # -# Captive portal / main page: -# install -D -v -m 644 files/html/ "${ROOTFS_DIR}/var/www/" +function gen_aos_service_sudoers() { + SERVICES=${1:-$AOS_SERVICES} + for srv in ${SERVICES}; do + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl disable ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl enable ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl start ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl stop ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl restart ${srv}" + done +} + +# Captive portal / main page mkdir -p "${ROOTFS_DIR}/var/www/" rsync -va files/html/ "${ROOTFS_DIR}/var/www/" rsync -va files/calfire_airbases/ "${ROOTFS_DIR}/var/www/html/" chmod +x "${ROOTFS_DIR}/var/www/html" -# Node-RED: +install -v -m 755 files/wifi-nuke.py "${ROOTFS_DIR}/usr/local/sbin/wifi-nuke.py" + +# Node-RED install -v -m 644 files/aryaos-flows.json "${ROOTFS_DIR}/home/node-red/.node-red/" cat "${ROOTFS_DIR}/home/node-red/.node-red/aryaos-flows.json" > "${ROOTFS_DIR}/home/node-red/.node-red/flows.json" -install -v -m 640 files/node-red.sudoers "${ROOTFS_DIR}/etc/sudoers.d/node-red" + +install -v -m 640 files/node-red.sudoers "${ROOTFS_DIR}/etc/sudoers.d/node-red" +SUDO_SERVICES="dump1090-fa dump978-fa gpsd comitup ${AOS_SERVICES}" +gen_aos_service_sudoers "${SUDO_SERVICES}" >> "${ROOTFS_DIR}/etc/sudoers.d/node-red" visudo -c "${ROOTFS_DIR}/etc/sudoers.d/node-red" -install -v -m 755 files/get_throttled.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 755 files/wifi-nuke.py "${ROOTFS_DIR}/usr/local/sbin/wifi-nuke.py" # LINCOT tracker -install -v -m 644 files/lincot-config.txt "${ROOTFS_DIR}/boot/" -install -v -m 755 files/run_lincot.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 755 files/get_position.sh "${ROOTFS_DIR}/usr/local/bin/" -install -v -m 644 files/lincot.service "${ROOTFS_DIR}/etc/systemd/system/" - -# Set UUID on first boot -install -v -m 755 files/set_uuid.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 644 files/set_uuid.service "${ROOTFS_DIR}/etc/systemd/system/" - -# AryaOS Env configuration -install -v -m 644 files/aryaos-config.txt "${ROOTFS_DIR}/boot/" -install -v -m 755 files/99-aryaos-dispatcher "${ROOTFS_DIR}/etc/NetworkManager/dispatcher.d/" -install -v -m 644 files/README-AryaOS.txt "${ROOTFS_DIR}/" - -# ZeroTier -install -v -m 755 files/install_zt.sh "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 755 files/get_position.sh "${ROOTFS_DIR}/usr/local/bin/" + +id lincot || useradd --system lincot + +export APP_NAME="LINCOT" +install -v -m 644 "files/${APP_NAME}-config.txt" "${ROOTFS_DIR}/boot/" +install -v -m 755 "files/run_${APP_NAME}.sh" "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 644 "files/${APP_NAME}.service" "${ROOTFS_DIR}/lib/systemd/system/" diff --git a/stage5-common/00-install/01-run-chroot.sh b/stage5-common/00-install/01-run-chroot.sh index 8aa9f4c..df17ba1 100755 --- a/stage5-common/00-install/01-run-chroot.sh +++ b/stage5-common/00-install/01-run-chroot.sh @@ -16,14 +16,7 @@ python3 -m pip install lincot --break-system-packages -systemctl enable lincot -systemctl enable set_uuid - -systemctl set-default multi-user - systemctl enable NetworkManager-dispatcher sed --follow-symlinks -i -E -e "s/blank.org/${AT_FLAVOR}.local/" /usr/share/comitup/web/templates/connect.html sed --follow-symlinks -i -E -e "s/aryaos.local/${AT_FLAVOR}.local/" /var/www/html/index.html - -/usr/local/sbin/install_zt.sh diff --git a/stage5-common/00-install/files/airtak-flows.json b/stage5-common/00-install/files/aryaos-flows.json similarity index 99% rename from stage5-common/00-install/files/airtak-flows.json rename to stage5-common/00-install/files/aryaos-flows.json index fc16050..4ee2dc3 100644 --- a/stage5-common/00-install/files/airtak-flows.json +++ b/stage5-common/00-install/files/aryaos-flows.json @@ -390,7 +390,7 @@ "id": "2ff2aec393c0b08b", "type": "ui_link", "name": "AryaOS Home", - "link": "http://aryaos.local", + "link": "http://AryaOS.local", "icon": "open_in_browser", "target": "newtab", "order": 1 @@ -2420,7 +2420,7 @@ "order": 1, "width": 0, "height": 0, - "format": "", + "format": "", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, @@ -3290,11 +3290,11 @@ "showgrid": "true", "showruler": "false", "allowFileDrop": "false", - "path": "/aryaos-map", + "path": "/AryaOS-map", "overlist": "DR,CO,RA,DN,BU,RW,SN,AC,TL,HM", "maplist": "OSMG,OSMC,OSMH,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,HB,AN,ST,SW", "mapname": "CalFire Airbases", - "mapurl": "http://aryaos.local/calfire_airbases/{z}/{x}/{y}.png", + "mapurl": "http://AryaOS.local/calfire_airbases/{z}/{x}/{y}.png", "mapopt": "", "mapwms": true, "x": 550, @@ -3355,7 +3355,7 @@ "overlist": "DR,CO,RA,DN,BU,RW,SN,AC,TL,HM", "maplist": "OSMG,OSMC,OSMH,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,HB,AN,ST,SW", "mapname": "CalFire Airbases", - "mapurl": "http://aryaos.local/calfire_airbases/{z}/{x}/{y}.png", + "mapurl": "http://AryaOS.local/calfire_airbases/{z}/{x}/{y}.png", "mapopt": "", "mapwms": true, "x": 550, @@ -3391,7 +3391,7 @@ "overlist": "DR,CO,RA,DN,HM", "maplist": "OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS", "mapname": "CalFire Airbases", - "mapurl": "http://aryaos.local/calfire_airbases/{z}/{x}/{y}.png", + "mapurl": "http://AryaOS.local/calfire_airbases/{z}/{x}/{y}.png", "mapopt": "", "mapwms": true, "x": 540, @@ -3565,7 +3565,7 @@ "type": "watch", "z": "76732e91104cd622", "name": "", - "files": "/boot/adsbcot-config.txt", + "files": "/boot/ADSBCOT-config.txt", "recursive": "", "x": 150, "y": 160, @@ -3580,7 +3580,7 @@ "id": "99875f9d26d1f50e", "type": "link out", "z": "76732e91104cd622", - "name": "adsbcot-config.txt Out", + "name": "ADSBCOT-config.txt Out", "mode": "link", "links": [ "09f4ad56eb3fd133" @@ -3593,7 +3593,7 @@ "id": "5f868835544d4788", "type": "inject", "z": "76732e91104cd622", - "name": "/boot/adsbcot-config.txt", + "name": "/boot/ADSBCOT-config.txt", "props": [ { "p": "payload" @@ -3604,7 +3604,7 @@ "once": true, "onceDelay": 0.1, "topic": "", - "payload": "/boot/adsbcot-config.txt", + "payload": "/boot/ADSBCOT-config.txt", "payloadType": "str", "x": 150, "y": 120, @@ -3619,7 +3619,7 @@ "type": "watch", "z": "76732e91104cd622", "name": "", - "files": "/boot/aryaos-config.txt", + "files": "/boot/AryaOS-config.txt", "recursive": "", "x": 140, "y": 260, @@ -3654,7 +3654,7 @@ "id": "b104d679e8967f58", "type": "link out", "z": "76732e91104cd622", - "name": "aryaos-config.txt Out", + "name": "AryaOS-config.txt Out", "mode": "link", "links": [ "a1642742e713d559", @@ -3679,7 +3679,7 @@ "once": true, "onceDelay": 0.1, "topic": "", - "payload": "/boot/aryaos-config.txt", + "payload": "/boot/AryaOS-config.txt", "payloadType": "str", "x": 140, "y": 220, @@ -3780,7 +3780,7 @@ "id": "8ef58d62e7fd687c", "type": "link out", "z": "76732e91104cd622", - "name": "tmp- -config.txt Out", + "name": "tmp-XXXX-config.txt Out", "mode": "link", "links": [ "df765e01751751eb" @@ -4361,7 +4361,7 @@ "type": "link in", "z": "4fa67ac9ec0c7369", "g": "c5409a5d2b5e28d5", - "name": "adsbcot-config.txt In", + "name": "ADSBCOT-config.txt In", "links": [ "8ef58d62e7fd687c" ], @@ -4527,7 +4527,7 @@ "type": "function", "z": "4fa67ac9ec0c7369", "name": "Set Configs", - "func": "let configs = {\n \"/boot/adsbcot-config.txt\": {\n FEED_URL: \"file:///run/dump1090-fa/aircraft.json\", \n POLL_INTERVAL: null, \n ALT_UPPER: null, \n ALT_LOWER: null\n },\n \"/boot/aryaos-config.txt\": {\n NODE_ID: null,\n COT_URL: \"udp+wo://239.2.3.1:6969\",\n PYTAK_MULTICAST_LOCAL_ADDR: \"0.0.0.0\",\n PYTAK_TLS_CLIENT_CERT: null,\n WIFI_AP_IP: \"10.41.0.1\",\n COT_ACCESS: \"UNCLASSIFIED\",\n STATIC_LAT: null,\n STATIC_LON: null,\n STATIC_ALT: null,\n },\n \"/boot/lincot-config.txt\": {\n GPS_INFO_CMD: null,\n CALLSIGN: null\n\n }\n}\n\nflow.set(\"configs\", configs)\n\nreturn msg;", + "func": "let configs = {\n \"/boot/ADSBCOT-config.txt\": {\n FEED_URL: \"file:///run/dump1090-fa/aircraft.json\", \n POLL_INTERVAL: null, \n ALT_UPPER: null, \n ALT_LOWER: null\n },\n \"/boot/AryaOS-config.txt\": {\n NODE_ID: null,\n COT_URL: \"udp+wo://239.2.3.1:6969\",\n PYTAK_MULTICAST_LOCAL_ADDR: \"0.0.0.0\",\n PYTAK_TLS_CLIENT_CERT: null,\n WIFI_AP_IP: \"10.41.0.1\",\n COT_ACCESS: \"UNCLASSIFIED\",\n STATIC_LAT: null,\n STATIC_LON: null,\n STATIC_ALT: null,\n },\n \"/boot/LINCOT-config.txt\": {\n GPS_INFO_CMD: null,\n CALLSIGN: null\n\n }\n}\n\nflow.set(\"configs\", configs)\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, @@ -4566,13 +4566,13 @@ "id": "9b8548552e7fbfa4", "type": "change", "z": "4fa67ac9ec0c7369", - "name": "/boot/adsbcot-config.txt", + "name": "/boot/ADSBCOT-config.txt", "rules": [ { "t": "set", "p": "filename", "pt": "msg", - "to": "/boot/adsbcot-config.txt", + "to": "/boot/ADSBCOT-config.txt", "tot": "str" } ], @@ -4593,13 +4593,13 @@ "id": "52d59a8a443b3363", "type": "change", "z": "4fa67ac9ec0c7369", - "name": "/boot/aryaos-config.txt", + "name": "/boot/AryaOS-config.txt", "rules": [ { "t": "set", "p": "filename", "pt": "msg", - "to": "/boot/aryaos-config.txt", + "to": "/boot/AryaOS-config.txt", "tot": "str" } ], @@ -4838,13 +4838,13 @@ "id": "68268787596399d6", "type": "change", "z": "4fa67ac9ec0c7369", - "name": "/boot/lincot-config.txt", + "name": "/boot/LINCOT-config.txt", "rules": [ { "t": "set", "p": "filename", "pt": "msg", - "to": "/boot/lincot-config.txt", + "to": "/boot/LINCOT-config.txt", "tot": "str" } ], diff --git a/stage5-common/00-install/files/get_position.sh b/stage5-common/00-install/files/get_position.sh index ff4cba0..cdfc803 100755 --- a/stage5-common/00-install/files/get_position.sh +++ b/stage5-common/00-install/files/get_position.sh @@ -17,13 +17,13 @@ # set -a -ARYAOS_CONF="/boot/aryaos-config.txt" +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" -if [[ -f ${ARYAOS_CONF} ]]; then - . ${ARYAOS_CONF} +if [-f $AOS_CONFIG ]; then + . $AOS_CONFIG else - echo "${ARYAOS_CONF} doesn't exist, exiting." + echo "$AOS_CONFIG doesn't exist, exiting." exit 1 fi -echo "{\"class\": \"TPV\", \"lat\": ${STATIC_LAT}, \"lon\": ${STATIC_LON}, \"altHAE\": ${STATIC_ALT}, \"track\": \"9999999.0\", \"speed\": \"9999999.0\"}" +echo "{\"class\": \"TPV\", \"lat\": $STATIC_LAT, \"lon\": $STATIC_LON, \"altHAE\": $STATIC_ALT, \"track\": \"9999999.0\", \"speed\": \"9999999.0\"}" diff --git a/stage5-common/00-install/files/lincot-config.txt b/stage5-common/00-install/files/lincot-config.txt index 28d6be2..c8ae669 100644 --- a/stage5-common/00-install/files/lincot-config.txt +++ b/stage5-common/00-install/files/lincot-config.txt @@ -1,4 +1,4 @@ -# AryaOS lincot-config.txt +# AryaOS LINCOT-config.txt # # LINCOT Env configuration file. # diff --git a/stage5-common/00-install/files/lincot.service b/stage5-common/00-install/files/lincot.service index 3d436c6..ade8ba0 100644 --- a/stage5-common/00-install/files/lincot.service +++ b/stage5-common/00-install/files/lincot.service @@ -1,6 +1,6 @@ -# AryaOS lincot.service +# AryaOS LINCOT.service # -# lincot service for systemd +# LINCOT service for systemd # # Copyright Sensors & Signals LLC https://www.snstac.com/ # @@ -17,15 +17,16 @@ [Unit] Description=LINCOT Linux GPSD to TAK Gateway -Documentation=https://github.com/snstac/lincot +Documentation=https://github.com/SNSTAC/LINCOT Wants=network.target After=network.target [Service] +User=lincot Type=simple RuntimeDirectoryMode=0755 -ExecStart=/usr/local/sbin/run_lincot.sh -SyslogIdentifier=lincot +ExecStart=/usr/local/sbin/run_LINCOT.sh +SyslogIdentifier=LINCOT KillSignal=SIGINT Restart=on-failure RestartSec=20 diff --git a/stage5-common/00-install/files/node-red.sudoers b/stage5-common/00-install/files/node-red.sudoers index 9cd2ba3..5296627 100644 --- a/stage5-common/00-install/files/node-red.sudoers +++ b/stage5-common/00-install/files/node-red.sudoers @@ -19,48 +19,9 @@ node-red ALL=(root) NOPASSWD: /sbin/shutdown node-red ALL=(root) NOPASSWD: /usr/bin/cp /home/node-red/tmpConf/aryaos-config.txt /boot/aryaos-config.txt node-red ALL=(root) NOPASSWD: /usr/bin/cp /home/node-red/tmpConf/adsbcot-config.txt /boot/adsbcot-config.txt node-red ALL=(root) NOPASSWD: /usr/bin/cp /home/node-red/tmpConf/lincot-config.txt /boot/lincot-config.txt +node-red ALL=(root) NOPASSWD: /usr/bin/cp /home/node-red/tmpConf/aiscot-config.txt /boot/aiscot-config.txt node-red ALL=(root) NOPASSWD: /usr/local/sbin/wifi-nuke.py node-red ALL=(root) NOPASSWD: /usr/bin/nmcli device disconnect wlan0 -node-red ALL=(root) NOPASSWD: /bin/systemctl disable comitup -node-red ALL=(root) NOPASSWD: /bin/systemctl enable comitup -node-red ALL=(root) NOPASSWD: /bin/systemctl stop comitup -node-red ALL=(root) NOPASSWD: /bin/systemctl start comitup -node-red ALL=(root) NOPASSWD: /bin/systemctl restart comitup - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable adsbcot -node-red ALL=(root) NOPASSWD: /bin/systemctl enable adsbcot -node-red ALL=(root) NOPASSWD: /bin/systemctl stop adsbcot -node-red ALL=(root) NOPASSWD: /bin/systemctl start adsbcot -node-red ALL=(root) NOPASSWD: /bin/systemctl restart adsbcot - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable dump1090-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl enable dump1090-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl stop dump1090-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl start dump1090-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl restart dump1090-fa - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable dump978-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl enable dump978-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl stop dump978-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl start dump978-fa -node-red ALL=(root) NOPASSWD: /bin/systemctl restart dump978-fa - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable lincot -node-red ALL=(root) NOPASSWD: /bin/systemctl enable lincot -node-red ALL=(root) NOPASSWD: /bin/systemctl stop lincot -node-red ALL=(root) NOPASSWD: /bin/systemctl start lincot -node-red ALL=(root) NOPASSWD: /bin/systemctl restart lincot - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable gpsd -node-red ALL=(root) NOPASSWD: /bin/systemctl enable gpsd -node-red ALL=(root) NOPASSWD: /bin/systemctl stop gpsd -node-red ALL=(root) NOPASSWD: /bin/systemctl start gpsd -node-red ALL=(root) NOPASSWD: /bin/systemctl restart gpsd - -node-red ALL=(root) NOPASSWD: /bin/systemctl disable nodered -node-red ALL=(root) NOPASSWD: /bin/systemctl enable nodered -node-red ALL=(root) NOPASSWD: /bin/systemctl stop nodered -node-red ALL=(root) NOPASSWD: /bin/systemctl start nodered -node-red ALL=(root) NOPASSWD: /bin/systemctl restart nodered +# Content below this line is generated: diff --git a/stage5-common/00-install/files/run_lincot.sh b/stage5-common/00-install/files/run_lincot.sh index 5b86419..fddb62a 100755 --- a/stage5-common/00-install/files/run_lincot.sh +++ b/stage5-common/00-install/files/run_lincot.sh @@ -1,5 +1,5 @@ #!/bin/bash -# AryaOS run_lincot.sh +# AryaOS run_LINCOT.sh # # Startup file for LINCOT. # @@ -16,25 +16,28 @@ # limitations under the License. # - set -a -ARYAOS_CONF="/boot/aryaos-config.txt" -LINCOT_CONF="/boot/lincot-config.txt" +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" -if [[ -f ${ARYAOS_CONF} ]]; then - . ${ARYAOS_CONF} +if [-f $AOS_CONFIG ]; then + . $AOS_CONFIG +else + echo "$AOS_CONFIG doesn't exist, exiting." + exit 1 fi -if [[ -f ${LINCOT_CONF} ]]; then - . ${LINCOT_CONF} +LINCOT_CONF="/boot/LINCOT-config.txt" + +if [ -f $LINCOT_CONF ]; then + . $LINCOT_CONF else - echo "${LINCOT_CONF} doesn't exist, initializing." - echo 'CALLSIGN=""' >> ${LINCOT_CONF} + echo "$LINCOT_CONF doesn't exist, initializing." + echo 'CALLSIGN=""' >> $LINCOT_CONF fi -if ! grep -qs -e 'CALLSIGN' ${LINCOT_CONF}; then - echo "Adding empty CALLSIGN to ${LINCOT_CONF}" - echo 'CALLSIGN=""' >> ${LINCOT_CONF} +if ! grep -qs -e 'CALLSIGN' $LINCOT_CONF; then + echo "Adding empty CALLSIGN to $LINCOT_CONF" + echo 'CALLSIGN=""' >> $LINCOT_CONF fi if [ -z "$NODE_ID" ]; then @@ -44,9 +47,9 @@ fi if [ -z "$CALLSIGN" ]; then echo "CALLSIGN not set" - NEW_CALLSIGN="AryaOS-${NODE_ID: -4}" - sed --follow-symlinks -i -E -e "s/CALLSIGN.*/CALLSIGN=$NEW_CALLSIGN/" ${LINCOT_CONF} - echo "CALLSIGN is now set to: $NEW_CALLSIGN" + NEW_CALLSIGN="${AOS_FLAVOR:-AryaOS}-${NODE_ID: -4}" + sed --follow-symlinks -i -E -e "s/CALLSIGN.*/CALLSIGN=$NEW_CALLSIGN/" $LINCOT_CONF + logger "AryaOS LINCOT callsign is now set to: $NEW_CALLSIGN" export CALLSIGN=$NEW_CALLSIGN fi diff --git a/stage5-common/00-install/test_00-run.sh b/stage5-common/00-install/test_00-run.sh new file mode 100755 index 0000000..4c3fa41 --- /dev/null +++ b/stage5-common/00-install/test_00-run.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e +# AryaOS test_00-run.sh +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +function gen_aos_service_sudoers() { + SERVICES=${1:-"$AOS_SERVICES"} + for srv in ${SERVICES}; do + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl disable ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl enable ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl start ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl stop ${srv}" + echo "node-red ALL=(root) NOPASSWD: /bin/systemctl restart ${srv}" + done +} + +SUDO_SERVICES="a b c d ${AOS_SERVICES}" +gen_aos_service_sudoers #"${SUDO_SERVICES}" \ No newline at end of file diff --git a/stage6-dump1090/00-sys-tweaks/00-run-chroot.sh b/stage6-air/00-sys-tweaks/00-run-chroot.sh similarity index 90% rename from stage6-dump1090/00-sys-tweaks/00-run-chroot.sh rename to stage6-air/00-sys-tweaks/00-run-chroot.sh index d43b853..13902df 100755 --- a/stage6-dump1090/00-sys-tweaks/00-run-chroot.sh +++ b/stage6-air/00-sys-tweaks/00-run-chroot.sh @@ -15,7 +15,6 @@ # apt update -apt dist-upgrade -y systemctl disable dphys-swapfile.service systemctl disable apt-daily.timer @@ -40,6 +39,3 @@ fi echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections export DEBIAN_FRONTEND=noninteractive - -# echo -n "${FIRST_USER_NAME:='pi'}:" > /boot/userconf.txt -# openssl passwd -5 "${FIRST_USER_PASS:='raspberry'}" >> /boot/userconf.txt diff --git a/stage6-dump1090/01-files/00-run.sh b/stage6-air/01-files/00-run.sh similarity index 100% rename from stage6-dump1090/01-files/00-run.sh rename to stage6-air/01-files/00-run.sh diff --git a/stage6-dump1090/01-files/files/blacklist-rtl-sdr.conf b/stage6-air/01-files/files/blacklist-rtl-sdr.conf similarity index 100% rename from stage6-dump1090/01-files/files/blacklist-rtl-sdr.conf rename to stage6-air/01-files/files/blacklist-rtl-sdr.conf diff --git a/stage6-dump1090/01-files/files/flightaware-apt-repository_1.2_all.deb b/stage6-air/01-files/files/flightaware-apt-repository_1.2_all.deb similarity index 100% rename from stage6-dump1090/01-files/files/flightaware-apt-repository_1.2_all.deb rename to stage6-air/01-files/files/flightaware-apt-repository_1.2_all.deb diff --git a/stage6-dump1090/02-install-repos/00-run-chroot.sh b/stage6-air/02-install-repos/00-run-chroot.sh similarity index 100% rename from stage6-dump1090/02-install-repos/00-run-chroot.sh rename to stage6-air/02-install-repos/00-run-chroot.sh diff --git a/stage6-dump1090/03-install-packages/00-packages b/stage6-air/03-install-packages/00-packages similarity index 100% rename from stage6-dump1090/03-install-packages/00-packages rename to stage6-air/03-install-packages/00-packages diff --git a/stage6-air/04-aryaosair/00-run.sh b/stage6-air/04-aryaosair/00-run.sh new file mode 100755 index 0000000..f310110 --- /dev/null +++ b/stage6-air/04-aryaosair/00-run.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +# AryaOS 00-run.sh +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +install -v -m 644 files/AryaAir.service "${ROOTFS_DIR}/lib/systemd/system/" +install -v -m 644 files/dump1090-fa.service "${ROOTFS_DIR}/lib/systemd/system/" +install -v -m 644 files/dump978-fa.service "${ROOTFS_DIR}/lib/systemd/system/" + +export APP_NAME="ADSBCOT" +install -v -m 644 "files/${APP_NAME}-config.txt" "${ROOTFS_DIR}/boot/" +install -v -m 755 "files/run_${APP_NAME}.sh" "${ROOTFS_DIR}/usr/local/sbin/" +install -v -m 644 "files/${APP_NAME}.service" "${ROOTFS_DIR}/lib/systemd/system/" diff --git a/stage6-dump1090/04-aryaos-custom/01-run-chroot.sh b/stage6-air/04-aryaosair/01-run-chroot.sh similarity index 65% rename from stage6-dump1090/04-aryaos-custom/01-run-chroot.sh rename to stage6-air/04-aryaosair/01-run-chroot.sh index 4ec3aeb..6c3b064 100755 --- a/stage6-dump1090/04-aryaos-custom/01-run-chroot.sh +++ b/stage6-air/04-aryaosair/01-run-chroot.sh @@ -14,5 +14,16 @@ # limitations under the License. # -sed --follow-symlinks -i -E -e "s/RECEIVER_SERIAL.*/RECEIVER_SERIAL=${DUMP1090_RECEIVER_SERIAL}/" /etc/default/dump1090-fa -sed --follow-symlinks -i -E -e "s/driver=rtlsdr /driver=rtlsdr,serial=${DUMP978_RECEIVER_SERIAL} /" /etc/default/dump978-fa +sed --follow-symlinks -i -E -e "s/RECEIVER_SERIAL.*/RECEIVER_SERIAL=$DUMP1090_RECEIVER_SERIAL/" /etc/default/dump1090-fa +sed --follow-symlinks -i -E -e "s/driver=rtlsdr /driver=rtlsdr,serial=$DUMP978_RECEIVER_SERIAL /" /etc/default/dump978-fa + +id adsbcot || useradd --system adsbcot + +python3 -m pip install aircot --break-system-packages +python3 -m pip install adsbcot --break-system-packages + +systemctl daemon-reload +systemctl disable dump1090-fa +systemctl disable dump978-fa +systemctl disable ADSBCOT +systemctl disable AryaAir \ No newline at end of file diff --git a/stage7-adsbcot/01-install-python-packages/files/adsbcot-config.txt b/stage6-air/04-aryaosair/files/ADSBCOT-config.txt similarity index 96% rename from stage7-adsbcot/01-install-python-packages/files/adsbcot-config.txt rename to stage6-air/04-aryaosair/files/ADSBCOT-config.txt index ab29801..99e17d8 100644 --- a/stage7-adsbcot/01-install-python-packages/files/adsbcot-config.txt +++ b/stage6-air/04-aryaosair/files/ADSBCOT-config.txt @@ -1,4 +1,4 @@ -# AryaOS adsbcot-config.txt +# AryaOS ADSBCOT-config.txt # # ADSBCOT Env configuration file. # diff --git a/stage7-adsbcot/01-install-python-packages/files/adsbcot.service b/stage6-air/04-aryaosair/files/ADSBCOT.service similarity index 71% rename from stage7-adsbcot/01-install-python-packages/files/adsbcot.service rename to stage6-air/04-aryaosair/files/ADSBCOT.service index 0077288..d04c5f4 100644 --- a/stage7-adsbcot/01-install-python-packages/files/adsbcot.service +++ b/stage6-air/04-aryaosair/files/ADSBCOT.service @@ -1,6 +1,6 @@ -# AryaOS adsbcot.service +# AryaOS ADSBCOT.service # -# adsbcot service for systemd +# ADSBCOT service for systemd # # Copyright Sensors & Signals LLC https://www.snstac.com/ # @@ -17,14 +17,16 @@ [Unit] Description=ADSBCOT: ADS-B to TAK Gateway -Documentation=https://github.com/snstac/adsbcot -Wants=network.target -After=network.target -After=dump1090-fa.service +Documentation=https://github.com/SNSTAC/ADSBCOT +PartOf=AryaAir.service +After=AryaAir.service [Service] -ExecStart=/usr/local/sbin/run_adsbcot.sh -SyslogIdentifier=adsbcot +User=adsbcot +RuntimeDirectory=ADSBCOT +RuntimeDirectoryMode=0755 +ExecStart=/usr/local/sbin/run_ADSBCOT.sh +SyslogIdentifier=ADSBCOT Type=simple Restart=on-failure RestartSec=20 @@ -32,4 +34,4 @@ RestartPreventExitStatus=64 Nice=-5 [Install] -WantedBy=default.target +WantedBy=AryaAir.target diff --git a/stage6-air/04-aryaosair/files/AryaAir.service b/stage6-air/04-aryaosair/files/AryaAir.service new file mode 100644 index 0000000..e814648 --- /dev/null +++ b/stage6-air/04-aryaosair/files/AryaAir.service @@ -0,0 +1,30 @@ +# AryaOS AryaSea.service +# +# Meta-app for AryaOS air SA services. +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +[Unit] +Description=AryaAir: Meta-app for AryaOS air SA services. +Documentation=https://github.com/SNSTAC/AryaOS +Wants=network.target +After=network.target + +[Service] +Type=oneshot +ExecStart=/bin/true +RemainAfterExit=yes + +[Install] +WantedBy=default.target diff --git a/stage6-air/04-aryaosair/files/dump1090-fa.service b/stage6-air/04-aryaosair/files/dump1090-fa.service new file mode 100644 index 0000000..ae0aae7 --- /dev/null +++ b/stage6-air/04-aryaosair/files/dump1090-fa.service @@ -0,0 +1,23 @@ +# AryaOS dump1090-fa.service +# dump1090-fa service for systemd + +[Unit] +Description=dump1090-fa ADS-B receiver +Documentation=https://flightaware.com/adsb/piaware/ +PartOf=AryaAir.service +After=AryaAir.service + +[Service] +User=dump1090 +RuntimeDirectory=dump1090-fa +RuntimeDirectoryMode=0755 +ExecStart=/usr/share/dump1090-fa/start-dump1090-fa --write-json %t/dump1090-fa +SyslogIdentifier=dump1090-fa +Type=simple +Restart=on-failure +RestartSec=30 +RestartPreventExitStatus=64 +Nice=-5 + +[Install] +WantedBy=AryaAir.target \ No newline at end of file diff --git a/stage6-air/04-aryaosair/files/dump978-fa.service b/stage6-air/04-aryaosair/files/dump978-fa.service new file mode 100644 index 0000000..57df89a --- /dev/null +++ b/stage6-air/04-aryaosair/files/dump978-fa.service @@ -0,0 +1,21 @@ +# AryaOS dump978-fa.service +# dump978-fa service for systemd + +[Unit] +Description=dump978-fa ADS-B UAT receiver +Documentation=https://flightaware.com/adsb/piaware/ +Wants=AryaAir.target +After=AryaAir.target + +[Service] +User=dump978 +ExecStart=/usr/share/dump978-fa/start-dump978-fa +SyslogIdentifier=dump978-fa +Type=simple +Restart=on-failure +RestartSec=30 +RestartPreventExitStatus=64 +Nice=-5 + +[Install] +WantedBy=AryaAir.target \ No newline at end of file diff --git a/stage7-adsbcot/01-install-python-packages/files/run_adsbcot.sh b/stage6-air/04-aryaosair/files/run_ADSBCOT.sh similarity index 76% rename from stage7-adsbcot/01-install-python-packages/files/run_adsbcot.sh rename to stage6-air/04-aryaosair/files/run_ADSBCOT.sh index 93d99eb..7f9c0e9 100755 --- a/stage7-adsbcot/01-install-python-packages/files/run_adsbcot.sh +++ b/stage6-air/04-aryaosair/files/run_ADSBCOT.sh @@ -1,4 +1,6 @@ #!/bin/bash +# AryaOS run_ADSBCOT.sh +# # Startup file for ADSBCOT. # # Copyright Sensors & Signals LLC https://www.snstac.com/ @@ -15,15 +17,15 @@ # set -a -AT_CONFIG="/boot/${AT_FLAVOR}-config.txt" -ADSBCOT_CONF="/boot/adsbcot-config.txt" +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" +ADSBCOT_CONFIG="/boot/ADSBCOT-config.txt" -if [[ -f ${AT_CONFIG} ]]; then - . ${AT_CONFIG} +if [ -f $AOS_CONFIG ]; then + . $AOS_CONFIG fi -if [[ -f ${ADSBCOT_CONF} ]]; then - . ${ADSBCOT_CONF} +if [ -f $ADSBCOT_CONFIG ]; then + . $ADSBCOT_CONFIG fi set +a diff --git a/stage6-dump1090/prerun.sh b/stage6-air/prerun.sh similarity index 100% rename from stage6-dump1090/prerun.sh rename to stage6-air/prerun.sh diff --git a/stage6-ais/00-install/files/start-ais.sh b/stage6-ais/00-install/files/start-ais.sh deleted file mode 100644 index c3c499f..0000000 --- a/stage6-ais/00-install/files/start-ais.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -CONFIG="" -while read -r line; do CONFIG="\${CONFIG} \$line"; done < ${INSTALL_FOLDER}/aiscatcher.conf -cd ${INSTALL_FOLDER} -/usr/local/bin/AIS-catcher \${CONFIG} diff --git a/stage7-adsbcot/00-install-packages/00-packages b/stage7-adsbcot/00-install-packages/00-packages deleted file mode 100644 index ff1dadf..0000000 --- a/stage7-adsbcot/00-install-packages/00-packages +++ /dev/null @@ -1,4 +0,0 @@ -python3-pip -libatlas-base-dev -rtl-sdr -librtlsdr-dev \ No newline at end of file diff --git a/stage6-ais/00-install/00-packages b/stage7-sea/00-install/00-packages similarity index 100% rename from stage6-ais/00-install/00-packages rename to stage7-sea/00-install/00-packages diff --git a/stage6-ais/00-install/00-run.sh b/stage7-sea/00-install/00-run.sh similarity index 68% rename from stage6-ais/00-install/00-run.sh rename to stage7-sea/00-install/00-run.sh index c6fa7a3..6f0be75 100755 --- a/stage6-ais/00-install/00-run.sh +++ b/stage7-sea/00-install/00-run.sh @@ -14,17 +14,12 @@ # limitations under the License. # +install -v -m 644 files/AryaSea.service "${ROOTFS_DIR}/lib/systemd/system/" install -v -m 755 files/uart_control "${ROOTFS_DIR}/home/pi" mkdir -p "${ROOTFS_DIR}/usr/share/aiscatcher" -install -v -m 644 files/aiscatcher.conf "${ROOTFS_DIR}/usr/share/aiscatcher" -install -v -m 644 files/aiscatcher.service "${ROOTFS_DIR}/lib/systemd/system" - -install -v -m 755 files/start-ais.sh "${ROOTFS_DIR}/usr/share/aiscatcher" - - CONFIG="${ROOTFS_DIR}/boot/config.txt" CMDLINE="${ROOTFS_DIR}/boot/cmdline.txt" @@ -41,3 +36,13 @@ if is_pithree; then else sed $CONFIG -i -e "s/^#dtoverlay=disable-bt/dtoverlay=disable-bt/" fi + +APP_NAME="AISCOT" +install -v -m 644 files/${APP_NAME}-config.txt "${ROOTFS_DIR}/boot/" +install -v -m 644 files/${APP_NAME}.service "${ROOTFS_DIR}/lib/systemd/system/" +install -v -m 755 files/run_${APP_NAME}.sh "${ROOTFS_DIR}/usr/local/sbin/" + +APP_NAME="aiscatcher" +install -v -m 644 files/${APP_NAME}.conf "${ROOTFS_DIR}/etc/" +install -v -m 644 files/${APP_NAME}.service "${ROOTFS_DIR}/lib/systemd/system/" +install -v -m 755 files/run_${APP_NAME}.sh "${ROOTFS_DIR}/usr/local/sbin/" diff --git a/stage6-ais/00-install/01-run-chroot.sh b/stage7-sea/00-install/01-run-chroot.sh similarity index 79% rename from stage6-ais/00-install/01-run-chroot.sh rename to stage7-sea/00-install/01-run-chroot.sh index 80a63fa..7c1fd94 100755 --- a/stage6-ais/00-install/01-run-chroot.sh +++ b/stage7-sea/00-install/01-run-chroot.sh @@ -14,8 +14,6 @@ # limitations under the License. # -# /home/pi/uart_control gpio - cd /usr/share/aiscatcher rm -rf AIS-catcher @@ -32,20 +30,24 @@ cmake .. make cd .. -cp /usr/share/aiscatcher/AIS-catcher/build/AIS-catcher /usr/local/bin/AIS-catcher +cp /usr/share/aiscatcher/AIS-catcher/build/AIS-catcher /usr/local/sbin/aiscatcher -mkdir my-plugins +cd /usr/share/aiscatcher +mkdir -p my-plugins cp AIS-catcher/plugins/* my-plugins/ -useradd --system aiscat +id aiscat || useradd --system aiscat usermod -a -G plugdev aiscat chown aiscat:aiscat -R /usr/share/aiscatcher systemctl disable hciuart -systemctl enable aiscatcher -systemctl start aiscatcher -# useradd -m -s "/bin/false" -c "AIS Dispatcher" ais || exit 0 -# usermod -a -G systemd-journal ais -# usermod -a -G dialout ais +id aiscot || useradd --system aiscot + +python3 -m pip install aiscot --break-system-packages + +systemctl daemon-reload +systemctl disable aiscatcher +systemctl disable AISCOT +systemctl disable AryaAir diff --git a/stage5-common/EXPORT_IMAGE b/stage7-sea/00-install/files/AISCOT-config.txt similarity index 78% rename from stage5-common/EXPORT_IMAGE rename to stage7-sea/00-install/files/AISCOT-config.txt index d9fe47b..6d54702 100644 --- a/stage5-common/EXPORT_IMAGE +++ b/stage7-sea/00-install/files/AISCOT-config.txt @@ -1,6 +1,6 @@ -# AryaOS EXPORT_IMAGE +# AryaOS AISCOT-config.txt # -# Export image configuration for AryaOS. +# AISCOT Env configuration file. # # Copyright Sensors & Signals LLC https://www.snstac.com/ # @@ -15,8 +15,4 @@ # limitations under the License. # -IMG_SUFFIX="-common" - -if [ "${USE_QEMU}" = "1" ]; then - export IMG_SUFFIX="${IMG_SUFFIX}-qemu" -fi +FEED_URL="" diff --git a/stage7-sea/00-install/files/AISCOT.service b/stage7-sea/00-install/files/AISCOT.service new file mode 100644 index 0000000..791d2e3 --- /dev/null +++ b/stage7-sea/00-install/files/AISCOT.service @@ -0,0 +1,37 @@ +# AryaOS AISCOT.service +# +# AISCOT service for systemd +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +[Unit] +Description=AISCOT: AIS to TAK Gateway +Documentation=https://github.com/SNSTAC/AISCOT +PartOf=AryaSea.service +After=AryaSea.service + +[Service] +User=aiscot +RuntimeDirectory=AISCOT +RuntimeDirectoryMode=0755 +ExecStart=/usr/local/sbin/run_AISCOT.sh +SyslogIdentifier=AISCOT +Type=simple +Restart=on-failure +RestartSec=20 +RestartPreventExitStatus=64 +Nice=-5 + +[Install] +WantedBy=AryaSea.service diff --git a/stage7-sea/00-install/files/AryaSea.service b/stage7-sea/00-install/files/AryaSea.service new file mode 100644 index 0000000..6da3846 --- /dev/null +++ b/stage7-sea/00-install/files/AryaSea.service @@ -0,0 +1,30 @@ +# AryaOS AryaSea.service +# +# Meta-app for AryaOS maritime SA services. +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +[Unit] +Description=AryaSea: Meta-app for AryaOS maritime SA services. +Documentation=https://github.com/SNSTAC/AryaOS +Wants=network.target +After=network.target + +[Service] +Type=oneshot +ExecStart=/bin/true +RemainAfterExit=yes + +[Install] +WantedBy=default.target diff --git a/stage6-ais/00-install/files/aiscatcher.conf b/stage7-sea/00-install/files/aiscatcher.conf similarity index 100% rename from stage6-ais/00-install/files/aiscatcher.conf rename to stage7-sea/00-install/files/aiscatcher.conf diff --git a/stage6-ais/00-install/files/aiscatcher.service b/stage7-sea/00-install/files/aiscatcher.service similarity index 82% rename from stage6-ais/00-install/files/aiscatcher.service rename to stage7-sea/00-install/files/aiscatcher.service index 0d0c83c..a1e760c 100644 --- a/stage6-ais/00-install/files/aiscatcher.service +++ b/stage7-sea/00-install/files/aiscatcher.service @@ -1,6 +1,6 @@ # AryaOS aiscatcher.service # -# AIS-catcher service for systemd +# aiscatcher service for systemd # # Copyright Sensors & Signals LLC https://www.snstac.com/ # @@ -16,15 +16,15 @@ # [Unit] -Description=AIS-catcher -Wants=network.target -After=network.target +Description=aiscatcher +PartOf=AryaSea.service +After=AryaSea.service [Service] User=aiscat RuntimeDirectory=aiscatcher RuntimeDirectoryMode=0755 -ExecStart=/bin/bash /usr/share/aiscatcher/start-ais.sh +ExecStart=/usr/local/sbin/run_aiscatcher.sh SyslogIdentifier=aiscatcher Type=simple Restart=on-failure @@ -33,4 +33,4 @@ RestartPreventExitStatus=64 Nice=-5 [Install] -WantedBy=default.target \ No newline at end of file +WantedBy=AryaSea.target \ No newline at end of file diff --git a/stage6-ais/00-install/files/install-aiscatcher.sh b/stage7-sea/00-install/files/install-aiscatcher.sh old mode 100644 new mode 100755 similarity index 100% rename from stage6-ais/00-install/files/install-aiscatcher.sh rename to stage7-sea/00-install/files/install-aiscatcher.sh diff --git a/stage6-ais/00-install/files/install_dispatcher b/stage7-sea/00-install/files/install_dispatcher similarity index 100% rename from stage6-ais/00-install/files/install_dispatcher rename to stage7-sea/00-install/files/install_dispatcher diff --git a/stage7-sea/00-install/files/run_AISCOT.sh b/stage7-sea/00-install/files/run_AISCOT.sh new file mode 100755 index 0000000..beed16c --- /dev/null +++ b/stage7-sea/00-install/files/run_AISCOT.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# AryaOS run_AISCOT.sh +# +# Startup file for AISCOT. +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -a +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" +AISCOT_CONFIG="/boot/AISCOT-config.txt" + +if [ -f $AOS_CONFIG ]; then + . $AOS_CONFIG +fi + +if [ -f $AISCOT_CONFIG ]; then + . $AISCOT_CONFIG +fi + +set +a +/usr/local/bin/aiscot diff --git a/stage7-sea/00-install/files/run_aiscatcher.sh b/stage7-sea/00-install/files/run_aiscatcher.sh new file mode 100755 index 0000000..0ef87bb --- /dev/null +++ b/stage7-sea/00-install/files/run_aiscatcher.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# AryaOS run_aiscatcher.sh +# +# Startup file for aiscatcher. +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -a +AOS_CONFIG="/boot/${AOS_FLAVOR:-AryaOS}-config.txt" + +CONFIG="" +while read -r line; do CONFIG="\${CONFIG} \$line"; done < /etc/aiscatcher.conf + +set +a +# cd ${INSTALL_FOLDER} +/usr/local/sbin/aiscatcher \${CONFIG} diff --git a/stage6-ais/00-install/files/uart_control b/stage7-sea/00-install/files/uart_control similarity index 100% rename from stage6-ais/00-install/files/uart_control rename to stage7-sea/00-install/files/uart_control diff --git a/stage6-ais/prerun.sh b/stage7-sea/prerun.sh similarity index 100% rename from stage6-ais/prerun.sh rename to stage7-sea/prerun.sh diff --git a/stage8-docker/00-prepare/00-packages b/stage8-docker/00-prepare/00-packages new file mode 100644 index 0000000..a779e5d --- /dev/null +++ b/stage8-docker/00-prepare/00-packages @@ -0,0 +1 @@ +ca-certificates diff --git a/stage7-adsbcot/01-install-python-packages/00-run.sh b/stage8-docker/00-prepare/00-run.sh similarity index 70% rename from stage7-adsbcot/01-install-python-packages/00-run.sh rename to stage8-docker/00-prepare/00-run.sh index 9b58bd3..5d172b0 100755 --- a/stage7-adsbcot/01-install-python-packages/00-run.sh +++ b/stage8-docker/00-prepare/00-run.sh @@ -14,6 +14,8 @@ # limitations under the License. # -install -v -m 644 files/adsbcot-config.txt "${ROOTFS_DIR}/boot/" -install -v -m 755 files/run_adsbcot.sh "${ROOTFS_DIR}/usr/local/sbin/" -install -v -m 644 files/adsbcot.service "${ROOTFS_DIR}/etc/systemd/system/" +install -m 0755 -d "${ROOTFS_DIR}/etc/apt/keyrings" +install -v -m 644 files/docker.asc "${ROOTFS_DIR}/etc/apt/keyrings/docker.asc" +chmod a+r "${ROOTFS_DIR}/etc/apt/keyrings/docker.asc" + +install -v -m 644 files/docker.list "${ROOTFS_DIR}/etc/apt/sources.list.d/docker.list" diff --git a/stage8-docker/00-prepare/01-run-chroot.sh b/stage8-docker/00-prepare/01-run-chroot.sh new file mode 100755 index 0000000..a7f0ec0 --- /dev/null +++ b/stage8-docker/00-prepare/01-run-chroot.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e +# AryaOS 01-run-chroot.sh +# +# Copyright Sensors & Signals LLC https://www.snstac.com/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apt update \ No newline at end of file diff --git a/stage8-docker/00-prepare/files/docker.asc b/stage8-docker/00-prepare/files/docker.asc new file mode 100644 index 0000000..ee7872e --- /dev/null +++ b/stage8-docker/00-prepare/files/docker.asc @@ -0,0 +1,62 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth +lqqwp028AqyY+PRfVMtSYMbjuQuu5byyKR01BbqYhuS3jtqQmljZ/bJvXqnmiVXh +38UuLa+z077PxyxQhu5BbqntTPQMfiyqEiU+BKbq2WmANUKQf+1AmZY/IruOXbnq +L4C1+gJ8vfmXQt99npCaxEjaNRVYfOS8QcixNzHUYnb6emjlANyEVlZzeqo7XKl7 +UrwV5inawTSzWNvtjEjj4nJL8NsLwscpLPQUhTQ+7BbQXAwAmeHCUTQIvvWXqw0N +cmhh4HgeQscQHYgOJjjDVfoY5MucvglbIgCqfzAHW9jxmRL4qbMZj+b1XoePEtht +ku4bIQN1X5P07fNWzlgaRL5Z4POXDDZTlIQ/El58j9kp4bnWRCJW0lya+f8ocodo +vZZ+Doi+fy4D5ZGrL4XEcIQP/Lv5uFyf+kQtl/94VFYVJOleAv8W92KdgDkhTcTD +G7c0tIkVEKNUq48b3aQ64NOZQW7fVjfoKwEZdOqPE72Pa45jrZzvUFxSpdiNk2tZ +XYukHjlxxEgBdC/J3cMMNRE1F4NCA3ApfV1Y7/hTeOnmDuDYwr9/obA8t016Yljj +q5rdkywPf4JF8mXUW5eCN1vAFHxeg9ZWemhBtQmGxXnw9M+z6hWwc6ahmwARAQAB +tCtEb2NrZXIgUmVsZWFzZSAoQ0UgZGViKSA8ZG9ja2VyQGRvY2tlci5jb20+iQI3 +BBMBCgAhBQJYrefAAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEI2BgDwO +v82IsskP/iQZo68flDQmNvn8X5XTd6RRaUH33kXYXquT6NkHJciS7E2gTJmqvMqd +tI4mNYHCSEYxI5qrcYV5YqX9P6+Ko+vozo4nseUQLPH/ATQ4qL0Zok+1jkag3Lgk +jonyUf9bwtWxFp05HC3GMHPhhcUSexCxQLQvnFWXD2sWLKivHp2fT8QbRGeZ+d3m +6fqcd5Fu7pxsqm0EUDK5NL+nPIgYhN+auTrhgzhK1CShfGccM/wfRlei9Utz6p9P +XRKIlWnXtT4qNGZNTN0tR+NLG/6Bqd8OYBaFAUcue/w1VW6JQ2VGYZHnZu9S8LMc +FYBa5Ig9PxwGQOgq6RDKDbV+PqTQT5EFMeR1mrjckk4DQJjbxeMZbiNMG5kGECA8 +g383P3elhn03WGbEEa4MNc3Z4+7c236QI3xWJfNPdUbXRaAwhy/6rTSFbzwKB0Jm +ebwzQfwjQY6f55MiI/RqDCyuPj3r3jyVRkK86pQKBAJwFHyqj9KaKXMZjfVnowLh +9svIGfNbGHpucATqREvUHuQbNnqkCx8VVhtYkhDb9fEP2xBu5VvHbR+3nfVhMut5 +G34Ct5RS7Jt6LIfFdtcn8CaSas/l1HbiGeRgc70X/9aYx/V/CEJv0lIe8gP6uDoW +FPIZ7d6vH+Vro6xuWEGiuMaiznap2KhZmpkgfupyFmplh0s6knymuQINBFit2ioB +EADneL9S9m4vhU3blaRjVUUyJ7b/qTjcSylvCH5XUE6R2k+ckEZjfAMZPLpO+/tF +M2JIJMD4SifKuS3xck9KtZGCufGmcwiLQRzeHF7vJUKrLD5RTkNi23ydvWZgPjtx +Q+DTT1Zcn7BrQFY6FgnRoUVIxwtdw1bMY/89rsFgS5wwuMESd3Q2RYgb7EOFOpnu +w6da7WakWf4IhnF5nsNYGDVaIHzpiqCl+uTbf1epCjrOlIzkZ3Z3Yk5CM/TiFzPk +z2lLz89cpD8U+NtCsfagWWfjd2U3jDapgH+7nQnCEWpROtzaKHG6lA3pXdix5zG8 +eRc6/0IbUSWvfjKxLLPfNeCS2pCL3IeEI5nothEEYdQH6szpLog79xB9dVnJyKJb +VfxXnseoYqVrRz2VVbUI5Blwm6B40E3eGVfUQWiux54DspyVMMk41Mx7QJ3iynIa +1N4ZAqVMAEruyXTRTxc9XW0tYhDMA/1GYvz0EmFpm8LzTHA6sFVtPm/ZlNCX6P1X +zJwrv7DSQKD6GGlBQUX+OeEJ8tTkkf8QTJSPUdh8P8YxDFS5EOGAvhhpMBYD42kQ +pqXjEC+XcycTvGI7impgv9PDY1RCC1zkBjKPa120rNhv/hkVk/YhuGoajoHyy4h7 +ZQopdcMtpN2dgmhEegny9JCSwxfQmQ0zK0g7m6SHiKMwjwARAQABiQQ+BBgBCAAJ +BQJYrdoqAhsCAikJEI2BgDwOv82IwV0gBBkBCAAGBQJYrdoqAAoJEH6gqcPyc/zY +1WAP/2wJ+R0gE6qsce3rjaIz58PJmc8goKrir5hnElWhPgbq7cYIsW5qiFyLhkdp +YcMmhD9mRiPpQn6Ya2w3e3B8zfIVKipbMBnke/ytZ9M7qHmDCcjoiSmwEXN3wKYI +mD9VHONsl/CG1rU9Isw1jtB5g1YxuBA7M/m36XN6x2u+NtNMDB9P56yc4gfsZVES +KA9v+yY2/l45L8d/WUkUi0YXomn6hyBGI7JrBLq0CX37GEYP6O9rrKipfz73XfO7 +JIGzOKZlljb/D9RX/g7nRbCn+3EtH7xnk+TK/50euEKw8SMUg147sJTcpQmv6UzZ +cM4JgL0HbHVCojV4C/plELwMddALOFeYQzTif6sMRPf+3DSj8frbInjChC3yOLy0 +6br92KFom17EIj2CAcoeq7UPhi2oouYBwPxh5ytdehJkoo+sN7RIWua6P2WSmon5 +U888cSylXC0+ADFdgLX9K2zrDVYUG1vo8CX0vzxFBaHwN6Px26fhIT1/hYUHQR1z +VfNDcyQmXqkOnZvvoMfz/Q0s9BhFJ/zU6AgQbIZE/hm1spsfgvtsD1frZfygXJ9f +irP+MSAI80xHSf91qSRZOj4Pl3ZJNbq4yYxv0b1pkMqeGdjdCYhLU+LZ4wbQmpCk +SVe2prlLureigXtmZfkqevRz7FrIZiu9ky8wnCAPwC7/zmS18rgP/17bOtL4/iIz +QhxAAoAMWVrGyJivSkjhSGx1uCojsWfsTAm11P7jsruIL61ZzMUVE2aM3Pmj5G+W +9AcZ58Em+1WsVnAXdUR//bMmhyr8wL/G1YO1V3JEJTRdxsSxdYa4deGBBY/Adpsw +24jxhOJR+lsJpqIUeb999+R8euDhRHG9eFO7DRu6weatUJ6suupoDTRWtr/4yGqe +dKxV3qQhNLSnaAzqW/1nA3iUB4k7kCaKZxhdhDbClf9P37qaRW467BLCVO/coL3y +Vm50dwdrNtKpMBh3ZpbB1uJvgi9mXtyBOMJ3v8RZeDzFiG8HdCtg9RvIt/AIFoHR +H3S+U79NT6i0KPzLImDfs8T7RlpyuMc4Ufs8ggyg9v3Ae6cN3eQyxcK3w0cbBwsh +/nQNfsA6uu+9H7NhbehBMhYnpNZyrHzCmzyXkauwRAqoCbGCNykTRwsur9gS41TQ +M8ssD1jFheOJf3hODnkKU+HKjvMROl1DK7zdmLdNzA1cvtZH/nCC9KPj1z8QC47S +xx+dTZSx4ONAhwbS/LN3PoKtn8LPjY9NP9uDWI+TWYquS2U+KHDrBDlsgozDbs/O +jCxcpDzNmXpWQHEtHU7649OXHP7UeNST1mCUCH5qdank0V1iejF6/CfTFU4MfcrG +YT90qFF93M3v01BbxP+EIY2/9tiIPbrd +=0YYh +-----END PGP PUBLIC KEY BLOCK----- diff --git a/stage8-docker/00-prepare/files/docker.list b/stage8-docker/00-prepare/files/docker.list new file mode 100644 index 0000000..3f8326f --- /dev/null +++ b/stage8-docker/00-prepare/files/docker.list @@ -0,0 +1 @@ +deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable \ No newline at end of file diff --git a/stage8-docker/01-install/00-packages b/stage8-docker/01-install/00-packages new file mode 100644 index 0000000..ed81c0e --- /dev/null +++ b/stage8-docker/01-install/00-packages @@ -0,0 +1,5 @@ +docker-ce +docker-ce-cli +containerd.io +docker-buildx-plugin +docker-compose-plugin diff --git a/stage7-adsbcot/EXPORT_IMAGE b/stage8-docker/EXPORT_IMAGE similarity index 100% rename from stage7-adsbcot/EXPORT_IMAGE rename to stage8-docker/EXPORT_IMAGE diff --git a/stage8-docker/prerun.sh b/stage8-docker/prerun.sh new file mode 100755 index 0000000..9acd13c --- /dev/null +++ b/stage8-docker/prerun.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +if [ ! -d "${ROOTFS_DIR}" ]; then + copy_previous +fi