Compare commits
71 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8cee0ec6f5 | ||
|
e8ac3e810a | ||
|
75185f19b6 | ||
|
64ff14b9b1 | ||
|
0a3c69a058 | ||
|
b469515f18 | ||
|
2dd401da6b | ||
|
41ffbe6d07 | ||
|
0ef5670247 | ||
|
4cef171e99 | ||
|
581cfd394a | ||
|
e289671c82 | ||
|
7cd41068fc | ||
|
a4839b8121 | ||
|
cab339c556 | ||
|
7d275fb9a2 | ||
|
f8433c1b23 | ||
|
bcaeff1a1c | ||
|
5bea85af12 | ||
|
3173308b54 | ||
|
49c4040072 | ||
|
6b092c1eea | ||
|
cb6d561c53 | ||
|
a2dfbf29fa | ||
|
79a4998058 | ||
|
4a62019556 | ||
|
30a5567a6c | ||
|
ccb72b1108 | ||
|
16a3609f9f | ||
|
e9d663963d | ||
|
e855429a8f | ||
|
0dc9e8e161 | ||
|
f658b27f7f | ||
|
8c9ea41bc5 | ||
|
99e3a06d39 | ||
|
d2498e8a04 | ||
|
72afd224e3 | ||
|
43ec20e38a | ||
|
739b86c1f2 | ||
|
7053edae14 | ||
|
e7e884d439 | ||
|
fbf455c5b7 | ||
|
4c1cfa6735 | ||
|
ccd46ee8a7 | ||
|
527965ac0f | ||
|
bd062da539 | ||
|
9a42d3b9bf | ||
|
44783b827c | ||
|
cf7d60f002 | ||
|
dbe08c033a | ||
|
fac9a142a9 | ||
|
f517ae1b42 | ||
|
0c9976565c | ||
|
c2b67e70c5 | ||
|
0f4f2438cb | ||
|
a7f5dd13c4 | ||
|
f72550146d | ||
|
afdfd3b85e | ||
|
11e5a9dd8f | ||
|
e5c9f277b6 | ||
|
aa6fb2fc8b | ||
|
0dbfc5fe6b | ||
|
81875a2cc5 | ||
|
f7a7dd37ed | ||
|
d555d55b2c | ||
|
d39ad4b112 | ||
|
8dd2c9485e | ||
|
c76abf967c | ||
|
aee44d32f0 | ||
|
3a997404a9 | ||
|
10e78bb41c |
4
.buildinfo
Normal file
|
@ -0,0 +1,4 @@
|
|||
# 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: 5fb11da93cdb0f49bc97733fbb9cadaf
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
254
.gitignore
vendored
|
@ -1,254 +0,0 @@
|
|||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,code
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,code
|
||||
|
||||
### Code ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### PyCharm ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### PyCharm Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
pytestdebug.log
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
doc/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
pythonenv*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# profiling data
|
||||
.prof
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,code
|
0
.nojekyll
Normal file
1
CNAME
Normal file
|
@ -0,0 +1 @@
|
|||
lib.comp-air.at
|
|
@ -1 +0,0 @@
|
|||
include readme.md
|
|
@ -1,8 +0,0 @@
|
|||
# compLIB
|
||||
|
||||
## TODOs
|
||||
|
||||
### LED control is not implemented
|
||||
|
||||
Need dependencies:
|
||||
https://github.com/Freenove/Freenove_RPI_WS281x_Python
|
BIN
_images/01_boot.png
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
_images/02_psk.png
Normal file
After Width: | Height: | Size: 239 KiB |
BIN
_images/03_codeServerFile.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
_images/04_codeServerRun.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
_images/05_codeServerTerminal.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
_images/06_codeServerFolder.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
_images/07_irSensor.webp
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
_images/08_notepad.png
Normal file
After Width: | Height: | Size: 156 KiB |
BIN
_images/09_update.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
_images/6x6_1000-0.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
_images/Mainboard.png
Normal file
After Width: | Height: | Size: 278 KiB |
BIN
_images/Sensorarray.png
Normal file
After Width: | Height: | Size: 254 KiB |
BIN
_images/chessboard.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
_images/chessboard_detected.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
189
_images/compair-logo-white.svg
Normal file
After Width: | Height: | Size: 188 KiB |
BIN
_images/opencv_http_stream.png
Normal file
After Width: | Height: | Size: 391 KiB |
BIN
_images/opencv_processed.png
Normal file
After Width: | Height: | Size: 356 KiB |
32
_sources/faq.rst.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
FAQ
|
||||
###
|
||||
|
||||
Was ist das Passwort für die Entwicklungsumgebung?
|
||||
--------------------------------------------------
|
||||
``compair``
|
||||
|
||||
Wie verbinde ich mich zur Entwicklungsumgebung?
|
||||
-----------------------------------------------
|
||||
|
||||
See :ref:`gettingstarted_codeserver`
|
||||
|
||||
Was ist der Benutzername und das Passwort für den Raspberry Pi?
|
||||
---------------------------------------------------------------
|
||||
``compair`` ``compair``
|
||||
|
||||
Wie aktualisiere ich meine Software?
|
||||
------------------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo update-firmware
|
||||
|
||||
Wie kann ich die SD-Karte neu beschreiben?
|
||||
------------------------------------------
|
||||
`SD-Karten Image <https://drive.google.com/drive/folders/16lMe-yGphk947L4WPjd4oD8ndY9R1WbA?usp=share_link>`_
|
||||
|
||||
Software zum Schreiben der SD-Karte `balenaEtcher <https://www.balena.io/etcher/>`_
|
||||
|
||||
|
11
_sources/gettingStarted/codeServer.rst.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
.. _gettingstarted_codeserver:
|
||||
|
||||
Programmierumgebung
|
||||
###################
|
||||
|
||||
Als Umgebung zur Programmierung des Roboters wird `code-server <https://github.com/coder/code-server>`_ eingesetzt, welche bereits am Roboter vorinstalliert ist.
|
||||
|
||||
Verbindung zur Entwicklungsumgebung herstellen
|
||||
----------------------------------------------
|
||||
Am Roboter wird die IP-Adresse des Raspberry Pi angezeigt. Um nun die Verbindung herzustellen, muss man in einem Web-Browser einfach ``<roboter_ip>:8080`` eingeben.
|
||||
Das Passwort für Visual Studio Code im Browser ist ``compair``!
|
26
_sources/gettingStarted/firstProgram.rst.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
Mein erstes Programm
|
||||
####################
|
||||
|
||||
Um mit der Programmierung zu beginnen, müssen wir zunächst einen neuen Ordner erstellen, in dem alle unsere Python-Dateien gespeichert werden.
|
||||
|codeServerFolder|
|
||||
|
||||
Sie können diesen Ordner nennen, wie Sie wollen, für dieses Beispiel heißt er ``compAIR``.
|
||||
Im nächsten Schritt erstellen wir unsere Datei ``main.py``.
|
||||
|codeServerFile|
|
||||
|
||||
Dann können wir beginnen, unseren Code in diese Datei zu schreiben.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print("Hallo Welt")
|
||||
|
||||
Praktischerweise können wir die Datei auch über die VS-Code-Plattform ausführen.
|
||||
|codeServerRun|
|
||||
|
||||
Dann öffnet sich ein Terminal, der die Ausgabe unseres Programms anzeigt.
|
||||
|codeServerTerminal|
|
||||
|
||||
.. |codeServerFolder| image:: images/06_codeServerFolder.png
|
||||
.. |codeServerFile| image:: images/03_codeServerFile.png
|
||||
.. |codeServerRun| image:: images/04_codeServerRun.png
|
||||
.. |codeServerTerminal| image:: images/05_codeServerTerminal.png
|
12
_sources/gettingStarted/index.rst.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
Erste Schritte
|
||||
##############
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 5
|
||||
|
||||
wifi.rst
|
||||
codeServer.rst
|
||||
firstProgram.rst
|
||||
update.rst
|
||||
secondProgram.rst
|
||||
thridProgram.rst
|
313
_sources/gettingStarted/secondProgram.rst.txt
Normal file
|
@ -0,0 +1,313 @@
|
|||
Mein zweites Programm
|
||||
#####################
|
||||
|
||||
Motoren ansteuern
|
||||
-----------------
|
||||
|
||||
Um die Motoren des Roboters zu steuern, müssen wir zunächst das entsprechende Python-Modul am Anfang der Datei importieren. Dann können wir Motor.power(port, power) verwenden, um den Motor zu steuern.
|
||||
Dies ist auch ein guter Punkt, um sich mit der Dokumentation vertraut zu machen: Besuchen wir https://lib.comp-air.at/lib/Motor.html#compLib.Motor.Motor.power. Hier werden die beiden relevanten Parameter beschrieben.
|
||||
|
||||
Als Beispiel wollen wir den rechten Motor für fünf Sekunden auf volle Geschwindigkeit setzen:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
# motor.py
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
Motor.power(0, 100)
|
||||
time.sleep(5)
|
||||
|
||||
Gerade fahren
|
||||
-------------
|
||||
Um geradeaus zu fahren, müssen wir beide Motoren auf dieselbe Geschwindigkeit einstellen.
|
||||
Aber Achtung! Der rechte Motor muss umgedreht werden! Das liegt daran, dass einer nach rechts und einer nach links zeigt, sie sind also technisch gesehen gespiegelt.
|
||||
Wenn wir nun diesen Code ausführen, wird der Roboter 5 Sekunden lang vorwärts fahren:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
# motor.py
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(5)
|
||||
|
||||
**Erläuterung**
|
||||
|
||||
| In ``Zeile 2`` wird das python-Paket ``time`` importiert. Wir brauchen es später, um auf die Fahrt des Roboters zu warten. Z.B.: ``time.sleep(5)``
|
||||
| In ``Zeile 3`` importieren wir die notwendigen Funktionen aus dem ``Motor``-Modul der compLib.
|
||||
| In ``Zeile 5`` stellen wir den ``rechten`` Motor so ein, dass er vorwärts fährt. Da der Motor rückwärts eingebaut ist, müssen wir den Wert auf ``-100`` setzen.
|
||||
| In ``Zeile 6`` stellen wir den ``linken`` Motor auf Vorwärtsfahrt ein. Hier können wir den Wert ``100`` verwenden, da der Motor in der richtigen Richtung eingebaut ist.
|
||||
| In ``Zeile 7`` müssen wir warten, bis der Roboter die Fahrbefehle tatsächlich ausführt. In diesem Fall warten wir ``5`` Sekunden lang.
|
||||
|
||||
Danach wird das Programm beendet und der Roboter bleibt stehen.
|
||||
|
||||
Mehr fahren
|
||||
+++++++++++
|
||||
|
||||
Jetzt ist es Zeit für einige komplexere Bewegungen. Um unseren Code modular und leicht lesbar zu halten, werden wir jede Aktion in eine eigene Funktion packen.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
In ``Zeile 4`` definieren wir die Funktion ``driveForward()``, die den Roboter mit voller Geschwindigkeit zwei Sekunden vorwärts bewegt.
|
||||
|
||||
Jetzt werden wir eine Funktion für das Rückwärtsfahren definieren:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
def driveBackward():
|
||||
Motor.power(0, 100)
|
||||
Motor.power(3, -100)
|
||||
time.sleep(2)
|
||||
|
||||
In ``Zeile 9`` haben wir die Funktion ``driveBackward()`` definiert, die den Roboter zwei Sekunden lang rückwärts fahren lässt.
|
||||
|
||||
Jetzt können wir diese beiden Funktionen aufrufen und vorwärts und dann wieder rückwärts fahren:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
def driveBackward():
|
||||
Motor.power(0, 100)
|
||||
Motor.power(3, -100)
|
||||
time.sleep(2)
|
||||
|
||||
driveForward()
|
||||
driveBackward()
|
||||
|
||||
Wenn wir diesen Code ausführen, sollte der Roboter zunächst zwei Sekunden vorwärts und dann wieder zwei Sekunden rückwärts fahren und ungefähr an der gleichen Position wie beim Start anhalten.
|
||||
|
||||
Zwischen den Zeilen ``14`` und ``15`` brauchen wir kein ``time.sleep(2)``, da der sleep-Befehl bereits in den Funktionen integriert ist.
|
||||
|
||||
Jetzt wollen wir, dass der Roboter erst vorwärts fährt, dann zwei Sekunden stillsteht und dann wieder rückwärts in seine Ausgangsposition fährt.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
def driveBackward():
|
||||
Motor.power(0, 100)
|
||||
Motor.power(3, -100)
|
||||
time.sleep(2)
|
||||
|
||||
driveForward()
|
||||
time.sleep(2)
|
||||
driveBackward()
|
||||
|
||||
Wenn wir den obigen Code ausführen, bleibt der Roboter nicht zwei Sekunden lang stehen, sondern fährt nach der Funktion ``driveForward()`` noch zwei Sekunden lang weiter. Warum passiert das? Um das zu verstehen, müssen wir wie der Roboter denken!
|
||||
|
||||
**Erläuterung**
|
||||
|
||||
| 1. (``Zeile 14``) Die Funktion Vorwärtsfahrt wird aufgerufen
|
||||
| (``Zeile 5``) Motor 1 wird auf -100 gesetzt
|
||||
| (``Zeile 6``) Motor 4 wird auf 100 gesetzt
|
||||
| (``Zeile 7``) Zwei Sekunden warten und Motor 1 mit der Geschwindigkeit -100 und Motor 4 mit der Geschwindigkeit 100 bewegen (z.B. vorwärts fahren)
|
||||
|
||||
| 2. (``Zeile 15``) Zwei Sekunden warten, die Motoren sind immer noch auf -100 und 100 eingestellt, also fahren wir weiter vorwärts
|
||||
|
||||
| 3. (``Zeile 16``) Die Funktion Rückwärtsfahren wird aufgerufen
|
||||
| (``Zeile 5``) Motor 1 wird auf 100 gesetzt
|
||||
| (``Zeile 6``) Motor 4 wird auf -100 gesetzt
|
||||
| (``Zeile 7``) Warte zwei Sekunden und bewege Motor 1 mit der Geschwindigkeit 100 und Motor 4 mit der Geschwindigkeit -100 (z.B. Rückwärtsfahren)
|
||||
|
||||
| 4. Das Programm ist beendet, und alle Motordrehzahlen werden auf 0 gesetzt.
|
||||
|
||||
Wir sehen also, dass wir die Motoren nach der Vorwärts- oder Rückwärtsfunktion wieder auf Geschwindigkeit ``0`` setzen müssen, wenn wir den Roboter anhalten wollen. Für diesen Anwendungsfall können wir eine neue Funktion ``stopMotors()`` schreiben, die die Geschwindigkeit für Motor ``0`` und ``3`` auf ``0`` setzt:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
def driveBackward():
|
||||
Motor.power(0, 100)
|
||||
Motor.power(3, -100)
|
||||
time.sleep(2)
|
||||
|
||||
def stopMotors():
|
||||
Motor.power(0, 0)
|
||||
Motor.power(3, 0)
|
||||
|
||||
Wenn wir nun vorwärts fahren, dann zwei Sekunden warten und dann wieder rückwärts fahren wollen, können wir die Funktionen wie folgt aufrufen:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
def driveBackward():
|
||||
Motor.power(0, 100)
|
||||
Motor.power(3, -100)
|
||||
time.sleep(2)
|
||||
|
||||
def stopMotors():
|
||||
Motor.power(0, 0)
|
||||
Motor.power(3, 0)
|
||||
|
||||
driveForward()
|
||||
stopMotors()
|
||||
time.sleep(2)
|
||||
driveBackward()
|
||||
|
||||
Und endlich bekommen wir die Bewegung, die wir uns wünschen.
|
||||
|
||||
**More Optimizations**
|
||||
|
||||
Während der Code für sehr einfache Bewegungen funktioniert, wollen wir normalerweise nicht, dass unsere Funktionen entscheiden, wie lange wir vorwärts fahren. Vielleicht müssen wir manchmal vier Sekunden vorwärts fahren, und manchmal nur eine Sekunde.
|
||||
|
||||
Nehmen wir an, wir wollen vier Sekunden vorwärts fahren. Wir wissen, dass ``driveForward()`` den Roboter zwei Sekunden vorwärts bewegen wird. Also können wir die Funktion einfach zwei Mal aufrufen!
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward():
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(2)
|
||||
|
||||
driveForward()
|
||||
driveForward()
|
||||
|
||||
Was aber, wenn wir uns nur eine Sekunde vorwärts bewegen wollen? Oder vielleicht drei Sekunden? Mit der Funktion ``driveForward()`` können wir das im Moment nicht machen.
|
||||
|
||||
Stattdessen werden wir die Funktion so umschreiben, dass sie einen Parameter akzeptiert, der die Zeit angibt.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward(seconds):
|
||||
Motor.power(0, -100)
|
||||
Motor.power(3, 100)
|
||||
time.sleep(seconds)
|
||||
|
||||
driveForward(3)
|
||||
|
||||
Und mit dieser neuen Funktion können wir drei Sekunden lang vorwärts fahren.
|
||||
Wie funktioniert das nun?
|
||||
|
||||
In ``Zeile 4`` definieren wir die Funktion ``driveForward`` und sagen, dass sie einen Parameter ``seconds`` benötigt. Dieser Parameter ist im Grunde eine Variable, die wir uns zum Zeitpunkt der Definition wie einen Platzhalter vorstellen können. Wenn wir die Funktion definieren, wissen wir noch nicht, welchen Wert ``seconds`` haben wird.
|
||||
|
||||
Später in ``Zeile 9``, wenn wir die Funktion aufrufen, übergeben wir den Wert ``3`` an die Funktion und unser Platzhalter ``seconds`` wird den Wert ``3`` haben. Der Roboter wird also drei Sekunden vorwärts fahren.
|
||||
|
||||
Vielleicht wollen wir auch, dass der Roboter mit verschiedenen Geschwindigkeiten fahren kann. Wir können also einen weiteren Parameter mit dem Namen ``speed`` anlegen. Dann werden wir ein Programm schreiben, das den Roboter drei Sekunden mit voller Geschwindigkeit und dann fünf Sekunden mit halber Geschwindigkeit fahren lässt.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward(seconds, speed):
|
||||
Motor.power(0, -speed)
|
||||
Motor.power(3, speed)
|
||||
time.sleep(seconds)
|
||||
|
||||
driveForward(3, 100)
|
||||
driveForward(5, 50)
|
||||
|
||||
In ``Zeile 9`` wird der Platzhalter ``seconds`` auf ``3`` und die ``Geschwindigkeit`` auf ``100`` gesetzt.
|
||||
In ``Zeile 10`` wird der Platzhalter ``seconds`` auf ``5`` und die ``Geschwindigkeit`` auf ``50`` gesetzt.
|
||||
|
||||
**Bewährte Praktiken**
|
||||
Nun werden wir uns einige weitere Optimierungen und bewährte Verfahren ansehen.
|
||||
|
||||
**1. Wir sollten den Schlafbefehl nicht in die Fahrfunktion einbauen.**
|
||||
|
||||
Wir haben das bis jetzt getan, um ein Gefühl dafür zu bekommen, wie Funktionen funktionieren, und der Einfachheit halber. Später, wenn Sie anfangen, komplexere Programme zu schreiben, sollten Sie dies vermeiden.
|
||||
|
||||
Das Beispiel von oben, in dem wir vorwärts und rückwärts gefahren sind und zwei Sekunden gewartet haben, sollte also wie folgt aussehen:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
|
||||
def driveForward(speed):
|
||||
Motor.power(0, -speed)
|
||||
Motor.power(3, speed)
|
||||
|
||||
def driveBackward(speed):
|
||||
Motor.power(0, speed)
|
||||
Motor.power(3, -speed)
|
||||
|
||||
def stopMotors():
|
||||
Motor.power(0, 0)
|
||||
Motor.power(3, 0)
|
||||
|
||||
driveForward(100) # Set the motors to forward
|
||||
time.sleep(2) # Let the robot drive for 2 seconds
|
||||
stopMotors() # Now stop the robot
|
||||
|
||||
time.sleep(2) # Wait another 2 seconds, robot is not moving
|
||||
|
||||
driveBackward(100) # Now set the motors to a backwards speed
|
||||
time.sleep(2) # Let the robot continue driving for 2 seconds
|
||||
stopMotors() # And finally stop it again
|
||||
|
||||
**Warum ist das so wichtig?**
|
||||
|
||||
Normalerweise schlafen wir nicht sehr viel und führen in dieser Zeit andere Verarbeitungen durch. Zum Beispiel könnten wir ein Bild von der Kamera verarbeiten oder die IR-Sensoren auslesen. Wenn wir also eine Funktion wie ``driveForward()`` aufrufen, können wir davon ausgehen, dass sie im Hintergrund abläuft und wir andere Aufgaben erledigen, während sich der Roboter bewegt, anstatt nur darauf zu warten, dass er fertig wird.
|
||||
|
||||
**2. Fahren Sie nicht zu langsam.**
|
||||
|
||||
Wenn du die Fahrgeschwindigkeit auf eine sehr kleine Zahl einstellst, kann es sein, dass sich der Roboter gar nicht mehr bewegt, weil die Motoren eine bestimmte Menge an Energie benötigen, um den Roboter überhaupt zu bewegen.
|
||||
|
||||
**3. Fahren Sie nicht zu schnell.**
|
||||
|
||||
Wenn du die Fahrgeschwindigkeit auf eine sehr hohe Zahl einstellst (z. B. ``100``), könnte dein Roboter zu schnell für seine Sensoren sein. Dies wird später wichtig sein, wenn wir versuchen, eine schwarze Linie zu erkennen, aber zu schnell über sie fahren.
|
70
_sources/gettingStarted/thridProgram.rst.txt
Normal file
|
@ -0,0 +1,70 @@
|
|||
Mein drittes Programm
|
||||
#####################
|
||||
|
||||
Der offizielle compAIR-Bot ist mit einer Reihe von Sensoren ausgestattet. Die wichtigsten sind die Infrarotsensoren und -sender, die an der Vorderseite des Roboters angebracht sind. Insgesamt gibt es fünf IR-Sensoren.
|
||||
|
||||
Um loszulegen, muss man zunächst das entsprechende Modul wie folgt importieren:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
|irSensor|
|
||||
|
||||
|
||||
Wie im obigen Diagramm zu sehen ist, verfügt jeder Sensor auch über einen entsprechenden IR-Sender / Emitter. Dieser Sender kann mit ``IRSensor.set(port, enable)`` aktiviert werden.
|
||||
|
||||
Schalten wir nun alle fünf Sender ein:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
IRSensor.enable()
|
||||
|
||||
Diese fünf verschiedenen Sensoren befinden sich an der Vorderseite des Roboters und sind wichtig, um schwarze Linien zu erkennen.
|
||||
|
||||
Es ist sehr einfach, den Wert der Sensoren abzulesen:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
IRSensor.enable()
|
||||
|
||||
if IRSensor.read_all()[0] > 500:
|
||||
print("high")
|
||||
else:
|
||||
print("low")
|
||||
|
||||
**Erkennen einer schwarzen Linie**
|
||||
Um den IR-Sensor zu testen, kannst du deinen Roboter auf eine schwarze Linie stellen. Der Sensor in der Mitte sollte auf der schwarzen Linie liegen.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
IRSensor.enable()
|
||||
COLOR_BREAK = 900
|
||||
|
||||
if IRSensor.read_all()[2] > COLOR_BREAK:
|
||||
print("Robot is standing on a black line")
|
||||
else:
|
||||
print("Robot is NOT standing on a black line")
|
||||
|
||||
Wenn das Programm ausgeführt wird, zeigt es an, dass der Roboter auf einer schwarzen Linie steht, wenn sich der mittlere IR-Sensor des Roboters über einer schwarzen Linie befindet, und es zeigt an, dass der Roboter NICHT auf einer schwarzen Linie steht, wenn sich der mittlere IR-Sensor nicht über einer Linie befindet.
|
||||
|
||||
| In ``Zeile 1`` importieren wir das ``IRSensor``-Modul, das zur Kommunikation mit dem IR-Sensor-Board verwendet werden kann.
|
||||
| In ``Zeile 3`` wird der Sensor mit der Nummer ``3`` aktiviert. Wenn wir einen Sensor nicht aktivieren, können wir ihn nicht in unserem Programm verwenden.
|
||||
| In ``Zeile 4`` stellen wir einen Farbschwellenwert von ``900`` ein, mit dem wir später prüfen werden, ob der Sensorwert unter oder über diesem Schwellenwert liegt. Unterhalb bedeutet, dass sich eine helle Farbe unter dem IR-Sensor befindet und ein höherer Wert als ``900`` bedeutet, dass sich eine dunkle Farbe unter dem IR-Sensor befindet.
|
||||
|
||||
| In ``Zeile 6`` lesen wir den Sensor Nummer ``2`` aus und prüfen, ob der Wert über dem von uns definierten Schwellenwert von ``900`` liegt. Wenn das der Fall ist, hat der IR-Sensor eine schwarze Linie erkannt.
|
||||
|
||||
Wir werden nun das Programm so ändern, dass es alle ``0.1`` Sekunden prüft, ob sich eine schwarze Linie unter dem Roboter befindet, und wenn dies der Fall ist, eine Meldung ausgibt.
|
||||
|
||||
|
||||
.. |irSensor| image:: images/07_irSensor.webp
|
13
_sources/gettingStarted/update.rst.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
Software Updaten
|
||||
#################
|
||||
|
||||
Da wir die ``compLib``, und die andere Software, welche auf dem Roboter läuft, laufend weiterentwickeln, solltet ihr immer wieder euren Roboter auf die neuste Version updaten. Dazu müsst ihr einfach den Roboter mit dem Internet verbinden und dann diesen Befehl in der Kommandozeile des Roboters eingeben:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update && sudo apt upgrade
|
||||
|
||||
Am einfachsten kann das über die Webseite gemacht werden, auf der ihr auch euren Code schreibt. Dazu müsst ihr einfach nur das Terminal (= Konsole) öffnen, dann den Befehl dort hineinkopieren und Enter drücken.
|
||||
|updatePic|
|
||||
|
||||
.. |updatePic| image:: images/09_update.png
|
86
_sources/gettingStarted/wifi.rst.txt
Normal file
|
@ -0,0 +1,86 @@
|
|||
.. _gettingStarted_wifi:
|
||||
|
||||
WLAN-Verbindung herstellen
|
||||
##########################
|
||||
|
||||
Schritt für Schritt - macOS
|
||||
---------------------------
|
||||
1. SD-Karte aus dem Raspberry Pi bzw. Roboter entnehmen.
|
||||
2. Einstecken der SD-Karte in den Computer
|
||||
3. Öffnen der SD-Karte mit dem Namen "boot" |bootImage|
|
||||
4. Generieren des PSK auf `https://www.wireshark.org/tools/wpa-psk.html <https://www.wireshark.org/tools/wpa-psk.html>`_ |pskImage|
|
||||
5. Öffnen der Datei "wpa_supplicant.conf" auf der SD-Karte
|
||||
6. Einfügen der Konfiguration. Dabei muss die SSID und der vorher generierte PSK eingesetzt werden ::
|
||||
|
||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||
update_config=1
|
||||
country=AT
|
||||
|
||||
network={
|
||||
ssid="EinTollerNameFürDasWlan"
|
||||
psk="98117b165a48f25cbe36f288ddf597729a40feeea93054c19bfa8e5eab238541"
|
||||
}
|
||||
|
||||
7. Speichern, Auswerfen und wieder in den Raspberry Pi einbauen
|
||||
8. Starten des Roboters
|
||||
9. Die IP-Adresse sollte nun am Roboter angezeigt werden
|
||||
|
||||
.. |bootImage| image:: images/01_boot.png
|
||||
.. |pskImage| image:: images/02_psk.png
|
||||
|
||||
Weitere Informationen
|
||||
---------------------
|
||||
Die "wpa_supplicant.conf" Datei wird beim Start des Rpasberry Pi automatisch an den richtigen Ort kopiert, damit sich der Roboter zum Wlan verbindet.
|
||||
Eine genauere Anleitung wird vom Hersteller des Raspberry Pi `hier <https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-networking-2>`_ bereitgestellt.
|
||||
|
||||
Windows......
|
||||
-------------
|
||||
Je nach Betriebssystem und Editor, mit dem Sie die Datei erstellen, könnte die Datei falsche Zeilenumbrüche oder eine falsche Dateierweiterung haben; stellen Sie also sicher, dass Sie einen Editor verwenden, der dies berücksichtigt. Linux erwartet das Zeilenumbruchzeichen LF (Line Feed).
|
||||
Beispielsweise kann `Notepad++ <https://notepad-plus-plus.org/downloads/>`_ verwendet werden, um die Datei richtig zu speichern.
|
||||
|notepadImage|
|
||||
|
||||
.. |notepadImage| image:: images/08_notepad.png
|
||||
|
||||
|
||||
Fehlerbehandlung
|
||||
----------------
|
||||
Sollte es dazu kommen, dass der Roboter nicht automatisch die Verbindung mit dem Netzwerk herstellt, kann eine Kabelgebundene Verbindung zur Diagnose von Fehlern genutzt werden.
|
||||
Dabei wird automatisch die IP-Adresse der Verbindung "eth" am Roboter angezeigt. Nach der erfolgreichen Verbindung zum Roboter mittels SSH kann die "wpa_cli" zur Fehlerbehandlung verwendet werden:
|
||||
::
|
||||
|
||||
> wpa_cli
|
||||
wpa_cli v2.9
|
||||
Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||
|
||||
This software may be distributed under the terms of the BSD license.
|
||||
See README for more details.
|
||||
|
||||
|
||||
Selected interface 'p2p-dev-wlan0'
|
||||
|
||||
Interactive mode
|
||||
|
||||
> interface wlan0
|
||||
Connected to interface 'wlan0.
|
||||
> scan
|
||||
OK
|
||||
<3>CTRL-EVENT-SCAN-STARTED
|
||||
<3>CTRL-EVENT-SCAN-RESULTS
|
||||
> scan_result
|
||||
bssid / frequency / signal level / flags / ssid
|
||||
68:02:b8:0c:d7:47 2462 -66 [WPA2-PSK-CCMP][ESS] WG
|
||||
68:02:b8:0c:d7:40 5220 -63 [WPA2-PSK-CCMP][ESS] WG
|
||||
34:2c:c4:da:dd:b9 5200 -65 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] WLAN10573403
|
||||
98:da:c4:e5:21:d0 2437 -57 [WPA2-PSK-CCMP][ESS] WG
|
||||
34:2c:c4:da:dd:c6 2412 -52 [WPA-PSK-][WPA2-PSK-CCMP+TKIP][WPS][ESS] WLAN10573403
|
||||
20:83:f8:07:5b:90 2467 -67 [WPA2-PSK-CCMP][WPS][ESS] A1-075b8c
|
||||
7c:39:53:94:49:82 5280 -77 [WPA2-PSK-CCMP][WPS][ESS] A1-944980-5G
|
||||
7c:39:53:94:49:81 2427 -68 [WPA2-PSK-CCMP][WPS][ESS] A1-944980
|
||||
90:fd:73:ac:d3:27 2452 -72 [WPA2-PSK-CCMP][WPS][ESS] Drei_H288A_24G_eKy5
|
||||
50:e0:39:3c:e5:80 5180 -82 [WPA2-PSK-CCMP][WPS][ESS] A1-393CE57F
|
||||
90:fd:73:ac:d3:28 5500 -83 [WPA2-PSK-CCMP][WPS][ESS] Drei_H288A_5G_eKy5
|
||||
68:02:b8:41:42:f9 5180 -84 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] WLAN18792472
|
||||
34:2c:c4:30:3c:65 5180 -89 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] witt
|
||||
fa:0d:ac:d3:40:d4 2467 -80 [WPA2-PSK-CCMP][WPS][ESS][P2P] DIRECT-d4-HP M28 LaserJet
|
||||
0e:84:dc:14:ac:27 2467 -85 [WPA2-PSK-CCMP][WPS][ESS][P2P] DIRECT-wo-BRAVIA
|
||||
>
|
20
_sources/index.rst.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
Dokumentation des Roboters
|
||||
##########################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
Inhalt
|
||||
******
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
gettingStarted/index.rst
|
||||
software/installation.rst
|
||||
faq.rst
|
||||
other/usage
|
||||
lib/index.rst
|
||||
other/hardware.rst
|
107
_sources/lib/Api.rst.txt
Normal file
|
@ -0,0 +1,107 @@
|
|||
.. _lib_api:
|
||||
|
||||
Api
|
||||
****
|
||||
|
||||
Seeding
|
||||
========
|
||||
|
||||
.. autoclass:: compLib.Api.Seeding
|
||||
:members:
|
||||
|
||||
Double Elimination
|
||||
===================
|
||||
|
||||
.. autoclass:: compLib.Api.DoubleElim
|
||||
:members:
|
||||
|
||||
Position
|
||||
========
|
||||
|
||||
.. autoclass:: compLib.Api.Position
|
||||
:members:
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Calling Seeding API
|
||||
---------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Api import Seeding
|
||||
|
||||
zones, code = Seeding.get_delivery()
|
||||
if code == 403:
|
||||
print(f"I am not in the correct zone to make that request!")
|
||||
else:
|
||||
print(f"First we need to go to zone {zone[0]}")
|
||||
# put code here to follow line and drive to the zone
|
||||
print(f"Now we need to go to zone {zone[1]}")
|
||||
# put code here to follow line and drive to the next zone
|
||||
print(f"Now we need to go to zone {zone[2]}")
|
||||
# put code here to follow line and drive to the last zone
|
||||
print(f"We delivered all packages, hopefully we scored some points!")
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Api import Seeding
|
||||
|
||||
package, code = Seeding.get_cargo("yellow")
|
||||
if code == 403:
|
||||
print(f"I am not in the correct zone to make that request!")
|
||||
elif code == 404:
|
||||
print(f"I am in the correct zone, but there is no yellow package here.")
|
||||
elif code == 413:
|
||||
print(f"I am in the correct zone, but I already have two packages loaded.")
|
||||
else code == 200:
|
||||
print(f"The {package['color']} has been picked up!")
|
||||
|
||||
Calling Double Elimination API
|
||||
----------------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Api import DoubleElim
|
||||
|
||||
position, status = DoubleElim.get_position()
|
||||
print(f"Position of my robot is: x={position.x}, y={position.y} and rotation is: {position.degrees}, the server responded with status code: {status}")
|
||||
|
||||
goal, status = DoubleElim.get_goal()
|
||||
print(f"Goal is at: x={goal.x}, y={goal.y}, the server responded with status code: {status}")
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Api import DoubleElim
|
||||
import time
|
||||
|
||||
# function which waits for the game to be started (you should include this in your double elimination program)
|
||||
def wait_for_start():
|
||||
_, status = DoubleElim.get_position()
|
||||
while status == 503:
|
||||
time.sleep(0.1)
|
||||
_, status = DoubleElim.get_position()
|
||||
|
||||
wait_for_start()
|
||||
print(f"Game has started, lets score some points!!")
|
||||
|
||||
position, status = DoubleElim.get_position()
|
||||
print(f"Position of my robot is: x={position.x}, y={position.y} and rotation is: {position.degrees}, the server responded with status code: {status}")
|
||||
|
||||
opponent_position, status = DoubleElim.get_opponent()
|
||||
print(f"Position of the opponents robot is: x={opponent_position.x}, y={opponent_position.y} and rotation is: {opponent_position.degrees}, the server responded with status code: {status}")
|
||||
|
||||
goal, status = DoubleElim.get_goal()
|
||||
print(f"Goal is at: x={goal.x}, y={goal.y}, the server responded with status code: {status}")
|
||||
|
||||
items, status = DoubleElim.get_items()
|
||||
print(f"There are currently {len(items)} on the gameboard: {items}, the server responded with status code: {status}")
|
||||
|
||||
score, status = DoubleElim.get_score()
|
||||
print(f"The current score of the game is {score}, the server responded with status code: {status}")
|
||||
|
||||
meteoroids, status = DoubleElim.get_meteoroids()
|
||||
print(f"The current meteoroids in the game are {meteoroids}, the server responded with status code: {status}")
|
||||
|
||||
In this second example we wait until the game is started by the judges and then make all possible requests once. You should use the wait_for_start function in your double elimination program. If your robot starts too soon your run will not count!
|
83
_sources/lib/Aruco.rst.txt
Normal file
|
@ -0,0 +1,83 @@
|
|||
.. _lib_vision:
|
||||
|
||||
Aruco
|
||||
*******
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Recognizing ArUco tags
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
import cv2
|
||||
from cv2 import aruco
|
||||
from compLib import Vision
|
||||
|
||||
ARUCO_DICT = cv2.aruco.Dictionary_get(aruco.DICT_6X6_250)
|
||||
ARUCO_PARAMETERS = aruco.DetectorParameters_create()
|
||||
|
||||
def getTagCenterFromFrame(id, frame):
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, ARUCO_DICT, parameters = ARUCO_PARAMETERS)
|
||||
frame = aruco.drawDetectedMarkers(frame.copy(), corners, ids)
|
||||
|
||||
if ids is None:
|
||||
return frame, None, None
|
||||
|
||||
for tag_id, corner in zip(ids, corners):
|
||||
if (tag_id[0] == id):
|
||||
|
||||
x, y = 0, 0
|
||||
for i in range(4):
|
||||
x += corner[0][i][0] * 0.25
|
||||
y += corner[0][i][1] * 0.25
|
||||
|
||||
return frame, x, y
|
||||
return frame, None, None
|
||||
|
||||
# Get the center from the aruco tag with the specified id
|
||||
# in pixel coordinates (0-640, 0-480)
|
||||
def getTagPosition(id):
|
||||
frame = Vision.Streaming.get_frame()
|
||||
frame, x, y = getTagCenterFromFrame(id, frame)
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
return x, y
|
||||
|
||||
# Get the normalized center coordinates from the aruco tag
|
||||
# with the specified id
|
||||
# left is -1, right +1
|
||||
# bottom is -1, top +1
|
||||
def getNormalizedTagPosition(id):
|
||||
frame = Vision.Streaming.get_frame()
|
||||
frame, x, y = getTagCenterFromFrame(id, frame)
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
|
||||
if x is None or y is None:
|
||||
return None, None
|
||||
|
||||
height, width = frame.shape[:2]
|
||||
x = x / width * 2.0 - 1.0
|
||||
y = -(y / height * 2.0 - 1.0)
|
||||
return x, y
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
desiredID = 11
|
||||
|
||||
while True:
|
||||
x, y = getNormalizedTagPosition(desiredID)
|
||||
|
||||
if x is not None:
|
||||
print("X Coordinate: ", x)
|
||||
else:
|
||||
print("Tag not found")
|
||||
|
||||
|
||||
This example shows how to recognize ArUco tags based on their id and position.
|
||||
You can specify an ID of the tag you want to use and if it's found, the coordinates of the center are returned.
|
||||
With the normalized function this is very easy: The x-coordinate is -1 on the left, 1 on the right and 0 in the center of the screen, same for y.
|
||||
This way it is quite simple to act on the position of the tag.
|
23
_sources/lib/Battery.rst.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
.. _lib_battery:
|
||||
|
||||
Battery
|
||||
********
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Battery.Battery
|
||||
:members:
|
||||
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Printing percentage
|
||||
--------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Battery import Battery
|
||||
|
||||
print(Battery.percent())
|
25
_sources/lib/Buzzer.rst.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
.. _lib_buzzer:
|
||||
|
||||
Buzzer
|
||||
*******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Buzzer.Buzzer
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Turning buzzer on and off
|
||||
--------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from compLib.Buzzer import Buzzer
|
||||
|
||||
Buzzer.set(True)
|
||||
time.sleep(1)
|
||||
Buzzer.set(False)
|
23
_sources/lib/Display.rst.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
.. _lib_display:
|
||||
|
||||
Display
|
||||
*******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Display.Display
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Write a line to the display
|
||||
---------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from compLib.Display import Display
|
||||
|
||||
Display.write(1, "Hello World!")
|
10
_sources/lib/Encoder.rst.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _lib_encoder:
|
||||
|
||||
Encoder
|
||||
*******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Encoder.Encoder
|
||||
:members:
|
20
_sources/lib/IRSensor.rst.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
.. _lib_irsensor:
|
||||
|
||||
Infrared Sensor
|
||||
****************
|
||||
|
||||
.. autoclass:: compLib.IRSensor.IRSensor
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Testing analog sensors
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib import IRSensor
|
||||
|
||||
while True:
|
||||
print ("left: {} middle: {} right: {}".format(IRSensor.read(1), IRSensor.read(3), IRSensor.read(5)))
|
22
_sources/lib/IRWrapper.rst.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
.. _lib_irwrapper:
|
||||
|
||||
Infrared Wrapper
|
||||
****************
|
||||
|
||||
.. autoclass:: compLib.IRWrapper.IRWrapper
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Calibrating analog sensors
|
||||
--------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib import IRWrapper
|
||||
|
||||
ir = IRWrapper.IRWrapper()
|
||||
|
||||
ir.calibrate()
|
||||
print("left {} middle {} right {}".format(ir.bottom_left_calibrated(), ir.bottom_middle_calibrated(), ir.bottom_right_calibrated()))
|
79
_sources/lib/Linefollower.rst.txt
Normal file
|
@ -0,0 +1,79 @@
|
|||
.. _lib_Linefollower:
|
||||
|
||||
Linefollower Examples
|
||||
*********************
|
||||
|
||||
Simple Linefollower
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Motor import Motor
|
||||
from compLib.Display import Display
|
||||
from compLib.IRSensor import IRSensor
|
||||
from compLib.Encoder import Encoder
|
||||
|
||||
import time
|
||||
|
||||
IRSensor.set(1, True)
|
||||
IRSensor.set(2, True)
|
||||
IRSensor.set(3, True)
|
||||
IRSensor.set(4, True)
|
||||
IRSensor.set(5, True)
|
||||
|
||||
DRIVE_SPEED = 75
|
||||
COLOR_BREAK = 900
|
||||
KP = 10.0
|
||||
KD = 0.0
|
||||
|
||||
def drive(leftSpeed, rightSpeed):
|
||||
rightSpeed *= -0.906
|
||||
|
||||
Motor.power(1, min(max(-100, rightSpeed), 100))
|
||||
Motor.power(4, min(max(-100, leftSpeed), 100))
|
||||
|
||||
def follow(sleepTime = 0.1):
|
||||
lastError = 0
|
||||
sensorsBlack = 0
|
||||
while sensorsBlack < 3:
|
||||
sensorsBlack = 0
|
||||
for i in range(1, 6):
|
||||
if IRSensor.read(i) > COLOR_BREAK:
|
||||
sensorsBlack += 1
|
||||
|
||||
error = lastError
|
||||
if IRSensor.read(3) > COLOR_BREAK:
|
||||
error = 0
|
||||
elif IRSensor.read(1) > COLOR_BREAK:
|
||||
error = -1.5
|
||||
elif IRSensor.read(5) > COLOR_BREAK:
|
||||
error = 1.5
|
||||
elif IRSensor.read(2) > COLOR_BREAK:
|
||||
error = -1
|
||||
elif IRSensor.read(4) > COLOR_BREAK:
|
||||
error = 1
|
||||
elif error == 1.5:
|
||||
error = 3
|
||||
elif error == -1.5:
|
||||
error = -3
|
||||
|
||||
lastError = error
|
||||
|
||||
adjustment = KP * error + KD * (error - lastError)
|
||||
leftSpeed = DRIVE_SPEED + adjustment
|
||||
rightSpeed = DRIVE_SPEED - adjustment
|
||||
|
||||
print(f"{leftSpeed} {rightSpeed} {adjustment} {error}")
|
||||
drive(leftSpeed, rightSpeed)
|
||||
|
||||
drive(0, 0)
|
||||
time.sleep(sleepTime)
|
||||
|
||||
def main():
|
||||
follow()
|
||||
follow()
|
||||
follow()
|
||||
follow()
|
||||
follow(0.2)
|
||||
|
||||
main()
|
22
_sources/lib/Logging.rst.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
.. _lib_logging:
|
||||
|
||||
Logging
|
||||
*******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.LogstashLogging.Logging
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Turn up the logging
|
||||
--------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.LogstashLogging import Logging
|
||||
|
||||
Logging.set_debug()
|
23
_sources/lib/Motor.rst.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
.. _lib_motor:
|
||||
|
||||
Motor
|
||||
******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Motor.Motor
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Driving straight (maybe)
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Motor import Motor
|
||||
|
||||
Motor.power(1, -50)
|
||||
Motor.power(4, 50)
|
50
_sources/lib/Odom.rst.txt
Normal file
|
@ -0,0 +1,50 @@
|
|||
.. _lib_odom:
|
||||
|
||||
Odometry
|
||||
********
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Odom.Odometry
|
||||
:members:
|
||||
|
||||
.. autoclass:: compLib.Odom.Odom
|
||||
:members:
|
||||
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Getting actual distance driven
|
||||
------------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
import math
|
||||
from compLib.Motor import Motor
|
||||
from compLib.Encoder import Encoder
|
||||
from compLib.Odom import Odom, Odometry
|
||||
|
||||
# distance in meters
|
||||
# speed in % of max speed
|
||||
def drive_example(distance, speed):
|
||||
Odom.update()
|
||||
odom = Odom.get_odom()
|
||||
while abs(odom.get_x()) < distance:
|
||||
Odom.update()
|
||||
odom = Odom.get_odom()
|
||||
|
||||
Motor.power(4, speed)
|
||||
Motor.power(1, -speed)
|
||||
|
||||
print(f" Forward: {odom.get_x()} m")
|
||||
print(f" Right: {odom.get_y()} m")
|
||||
print(f" Turned: {math.degrees(odom.get_orientation())} degrees")
|
||||
|
||||
Motor.active_break(1)
|
||||
Motor.active_break(4)
|
||||
time.sleep(0.1)
|
||||
Encoder.clear_all()
|
||||
Odom.clear()
|
71
_sources/lib/QC.rst.txt
Normal file
|
@ -0,0 +1,71 @@
|
|||
.. _lib_qc:
|
||||
|
||||
Quality Control
|
||||
###############
|
||||
|
||||
Infrared Test
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.IRSensor import IRSensor
|
||||
import time
|
||||
|
||||
IRSensor.set(1, True)
|
||||
IRSensor.set(2, True)
|
||||
IRSensor.set(3, True)
|
||||
IRSensor.set(4, True)
|
||||
IRSensor.set(5, True)
|
||||
|
||||
while True:
|
||||
t = time.time()
|
||||
for i in range(1, 6):
|
||||
print(f"{i}: {IRSensor.read(i)}")
|
||||
print("")
|
||||
time.sleep(0.2)
|
||||
|
||||
Motor Test
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Motor import Motor
|
||||
from compLib.Encoder import Encoder
|
||||
import time
|
||||
|
||||
Motor.power(1, -50)
|
||||
Motor.power(4, 50)
|
||||
|
||||
while True:
|
||||
print(f"L:{Encoder.read(4)} R:{Encoder.read(1)}")
|
||||
time.sleep(0.1)
|
||||
|
||||
Servo Test
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Servo import Servo
|
||||
import time
|
||||
|
||||
for i in range(1, 8 + 1):
|
||||
Servo.set_position(i, 45)
|
||||
print(f"{i}")
|
||||
time.sleep(1)
|
||||
|
||||
Servo.setup_position()
|
||||
time.sleep(10)
|
||||
|
||||
Vision Test
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cv2
|
||||
from compLib import Vision
|
||||
from compLib.Servo import Servo
|
||||
|
||||
while True:
|
||||
frame = Vision.Streaming.get_frame()
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
Servo.set_position(3, -45)
|
11
_sources/lib/Robot.rst.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
.. _lib_robot:
|
||||
|
||||
Robot
|
||||
******
|
||||
|
||||
Class Documentation
|
||||
====================
|
||||
|
||||
.. autoclass:: compLib.Robot.Robot
|
||||
:members:
|
||||
:private-members:
|
7
_sources/lib/Servo.rst.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
.. _lib_servo:
|
||||
|
||||
Servo
|
||||
******
|
||||
|
||||
.. autoclass:: compLib.Servo.Servo
|
||||
:members:
|
104
_sources/lib/Vision.rst.txt
Normal file
|
@ -0,0 +1,104 @@
|
|||
.. _lib_vision:
|
||||
|
||||
Vision
|
||||
*******
|
||||
|
||||
This module provides an interface for grabbing an rtmp stream and using the images to do some processing in opencv.
|
||||
|
||||
How do I use this module?
|
||||
|
||||
1. Get frames from the raspberry pi camera
|
||||
2. -- here comes your own processing --
|
||||
3. Publish the processed frames on an http server
|
||||
4. You can view the http stream of your processed images in a web browser
|
||||
|
||||
Opencv Stream
|
||||
==============
|
||||
|
||||
Because of the rtmp stream needing to buffer some frames and waiting for P-Frames, importing this module might take up
|
||||
to 5 Seconds.
|
||||
|
||||
.. autoclass:: compLib.Vision.__Streaming
|
||||
:members:
|
||||
|
||||
Examples
|
||||
=========
|
||||
|
||||
Using the Vision Module
|
||||
-------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cv2
|
||||
from compLib import Vision
|
||||
|
||||
while True:
|
||||
# get newest opencv frame from camera
|
||||
frame = Vision.Streaming.get_frame()
|
||||
|
||||
# do some processing with the frame.....
|
||||
|
||||
# publish frame to streaming server
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
|
||||
Connect the raspberry pi to your internet and view the stream at: "http://your_raspi_ip:9898/". This should display
|
||||
your raspberry pi camera. Note: the stream will lag a little bit BUT the processing of the image will be done in
|
||||
realtime.
|
||||
|
||||
The output on the website should show whatever your raspberry pi cam records:
|
||||
|
||||
.. image:: images/opencv_http_stream.png
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
|
||||
Chessboard Detection
|
||||
------------------------------------------
|
||||
|
||||
In this example we process the captured stream of images and want to detect chessboards. Run this example and
|
||||
point your raspberry pi camera to a chessboard and it should be detected.
|
||||
|
||||
For testing you can point it at this image:
|
||||
|
||||
.. image:: images/chessboard.jpg
|
||||
:width: 680
|
||||
:alt: Chessboard for opencv processing
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cv2
|
||||
from compLib import Vision
|
||||
|
||||
while True:
|
||||
# get newest opencv frame from camera
|
||||
frame = Vision.Streaming.get_frame()
|
||||
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
|
||||
# convert image to grayscale image
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# find the chessboard corners
|
||||
ret, corners = cv2.findChessboardCorners(gray, (6, 9), None)
|
||||
|
||||
# draw detected chessboard position onto the image
|
||||
cv2.drawChessboardCorners(frame, (6, 9), corners, ret)
|
||||
|
||||
# publish frame to streaming server
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
|
||||
Connect the raspberry pi to your internet and view the stream at: "http://your_raspi_ip:9898/".
|
||||
|
||||
The output image should look like this:
|
||||
|
||||
.. image:: images/chessboard_detected.jpg
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
Here is a screenshot of the stream website while viewing the chessboard in this documentation.
|
||||
|
||||
.. image:: images/opencv_processed.png
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
|
13
_sources/lib/classes/DoubleElimination.rst.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
.. _lib_doubleElim:
|
||||
|
||||
Double Elimination
|
||||
*******************
|
||||
|
||||
Dokumentation des Double Elimination Moduls
|
||||
============================================
|
||||
|
||||
.. autoclass:: compLib.DoubleElimination.Position
|
||||
:members:
|
||||
|
||||
.. autoclass:: compLib.DoubleElimination.DoubleElim
|
||||
:members:
|
10
_sources/lib/classes/Encoder.rst.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _lib_encoder:
|
||||
|
||||
Encoder
|
||||
*******
|
||||
|
||||
Dokumentation der Klasse
|
||||
========================
|
||||
|
||||
.. autoclass:: compLib.Encoder.Encoder
|
||||
:members:
|
10
_sources/lib/classes/IRSensor.rst.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _lib_irsensor:
|
||||
|
||||
Infrarot Sensoren
|
||||
*****************
|
||||
|
||||
Dokumentation der Klasse
|
||||
========================
|
||||
|
||||
.. autoclass:: compLib.IRSensor.IRSensor
|
||||
:members:
|
50
_sources/lib/classes/Motor.rst.txt
Normal file
|
@ -0,0 +1,50 @@
|
|||
.. _lib_motor:
|
||||
|
||||
Motoren
|
||||
********
|
||||
|
||||
Dokumentation der Klasse
|
||||
========================
|
||||
|
||||
.. autoclass:: compLib.Motor.Motor
|
||||
:members:
|
||||
|
||||
Genauere Informationen
|
||||
======================
|
||||
|
||||
Power vs Speed vs PulseWidth
|
||||
-----------------------------
|
||||
Zur ansteuerung der Motoren kann entweder ``Motor.power(...)``, ``Motor.speed(...)`` oder ``Motor.pulse_width(...)``` verwendet werden.
|
||||
Der Unterschied der 3 Funktionen liegt dabei in der Einheit des 2. Parameters.
|
||||
|
||||
| Bei ``Motor.power()`` wird dabei ein Wert zwischen -100% und 100% der maximalen Geschwindigkeit angegeben.
|
||||
| ``Motor.speed()`` verwendet die Encoder um die Geschwindigkeit der Motoren mittels closed-loop zu steuern. Diese Funktion sollte nur verwendet werden, wenn ``Motor.power()`` nicht zur Ansteuerung ausreicht.
|
||||
| ``Motor.pulse_width()`` stellt die Geschwindigkeit des Motors mittels der Pulsbreite der PWM-Steuerung des Motors ein. Diese Funktion ist so nah an der Hardware wie möglich und sollte auch nur verwendet werden, wenn es einen expliziten Grund dafür gibt.
|
||||
|
||||
Normal vs Multiple
|
||||
------------------
|
||||
Der Aufruf der funktionen kann entweder über ``Motor.power(port, percent)`` oder ``Motor.power((port, percent), (port, percent), ..)`` erfolgen.
|
||||
Der zweite Aufruf ermöglicht dem Entwickler dabei beide Motoren in einem Aufruf anzusteuern und bringt einen kleinen Vorteil in der Leistungsfähigkeit der Software.
|
||||
|
||||
|
||||
Beispiele
|
||||
=========
|
||||
|
||||
Vorwärts fahren
|
||||
---------------
|
||||
|
||||
Mit folgenden Programm drehen sich beide Motoren mit 50% ihrer maximalen Geschwindigkeit.
|
||||
Dabei ist zu beachten, dass ein Motor in die entgegengesetzte Richtung zum aneren Motor gedreht werden muss, da diese spiegelverkehrt montiert sind.
|
||||
|
||||
Zusätzlich ist ein ``time.sleep(5)`` notwendig, welches das Programm für 5 Sekunden pausiert. Diese Pause wird benötigt, da der Roboter automatisch alle Motoren beim Ende des Progammes deaktiviert.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Motor import Motor
|
||||
import time
|
||||
|
||||
Motor.power(0, -50)
|
||||
Motor.power(3, 50)
|
||||
|
||||
time.sleep(5)
|
||||
|
79
_sources/lib/classes/Opencv.rst.txt
Normal file
|
@ -0,0 +1,79 @@
|
|||
.. _lib_camera:
|
||||
|
||||
Camera und OpenCV
|
||||
*******************
|
||||
|
||||
Dokumentation des Camera Moduls
|
||||
================================
|
||||
|
||||
.. autoclass:: compLib.Camera.Marker
|
||||
:members:
|
||||
|
||||
.. autoclass:: compLib.Camera.Camera
|
||||
:members:
|
||||
|
||||
Beispiele
|
||||
=========
|
||||
|
||||
Bild Anzeigen
|
||||
---------------
|
||||
|
||||
Das folgende Programm fragt Bilder von der Kamera ab und schickt sie an den Webserver, der im Hintergrund läuft. Der Benutzer kann dann auf die Webseite: http://raspi_ip:9898 gehen und die Ausgabe der Kamera sehen.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Camera import *
|
||||
|
||||
camera = Camera()
|
||||
while True:
|
||||
image = camera.get_frame()
|
||||
camera.publish_frame(image)
|
||||
|
||||
ArUco Marker Erkennen
|
||||
------------------------
|
||||
|
||||
In diesem Programm werden die ArUco Marker, die sich am Spielfeld befinden, erkannt. Diese "QR-Code" ähnlichen Marker finden sich in den Logistikzonen und können dazu verwendet werden zu erkennen, wo der Roboter hinfahren sollt etc.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Camera import *
|
||||
|
||||
camera = Camera()
|
||||
while True:
|
||||
image = camera.get_frame()
|
||||
|
||||
markers, image = camera.detect_markers_midpoint(image)
|
||||
print(markers)
|
||||
print("-----------------")
|
||||
|
||||
camera.publish_frame(image)
|
||||
|
||||
Hier ist z.B. der ArUco Marker mit der ID 0. Führe das Programm aus und lass den Roboter auf den Bildschirm schauen. Das Programm sollte die 2D Position ausgeben, welcher der ArUco Marker (genauer sein Mittelpunkt) im Camera Bild hat.
|
||||
|
||||
|ArucoExample|
|
||||
|
||||
.. |ArucoExample| image:: images/6x6_1000-0.png
|
||||
|
||||
Um die Positionen zu verarbeiten, muss dann nur noch das "markers" array durchgegangen werden. Das könnte z.B. so gemacht werden:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from compLib.Camera import *
|
||||
|
||||
camera = Camera()
|
||||
while True:
|
||||
image = camera.get_frame()
|
||||
|
||||
markers, image = camera.detect_markers_midpoint(image)
|
||||
print(markers)
|
||||
print("-----------------")
|
||||
|
||||
for marker in markers:
|
||||
print(f"Marker mit der id: {marker.id}")
|
||||
print(f"Ist auf der X Position: {marker.x}")
|
||||
print(f"und auf der Y Position: {marker.y}")
|
||||
print("-----------------")
|
||||
|
||||
camera.publish_frame(image)
|
||||
|
||||
Wichtig ist noch zu beachten, dass die X und Y Positionen ihren Ursprung in der linken oberen Ecke des Bildes haben. D.h. die Position (0,0) ist im oberen linken Bildrand.
|
36
_sources/lib/classes/Seeding.rst.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
.. _lib_seeding:
|
||||
|
||||
Seeding
|
||||
*******
|
||||
|
||||
Dokumentation des Seeding Moduls
|
||||
================================
|
||||
|
||||
.. autoclass:: compLib.Seeding.Gamestate
|
||||
:members:
|
||||
|
||||
Beispiele
|
||||
----------
|
||||
|
||||
| In ``Zeile 1`` wird das Seeding Modul importiert.
|
||||
| In ``Zeile 2`` definieren wir dann eine Variable, in der wir den "Seed" des Gamestates den wir erstellen wollten speichern.
|
||||
| In ``Zeile 3`` erstellen wir dann einen neuen Gamestate mit dem Seed und speichern ihn in die Variable ``gamestate``.
|
||||
| In ``Zeile 4`` geben wir dann den Gamestate aus, damit wir ihn auf der Konsole ansehen können.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import compLib.Seeding as Seeding
|
||||
seed = 42
|
||||
gamestate = Seeding.Gamestate(seed)
|
||||
print(gamestate)
|
||||
|
||||
In der Ausgabe des Print Statements sehen wir den generierten Gamestate.
|
||||
|
||||
.. code-block::
|
||||
|
||||
Seed: 42
|
||||
Heu Color: 1
|
||||
Material Pairs: [[3, 0], [2, 3], [0, 2], [1, 2]]
|
||||
Material Zones: [2, 1, 3, 2]
|
||||
Logistic Plan: [12, 13, 12, 13, 10, 11, 13, 10, 13, 12, 11, 10, 11, 13, 10, 11, 12, 11, 12, 10, 12]
|
||||
Logistic Centers: [[0, 3, 1, 1], [1, 0, 2, 2], [1, 2, 0, 2], [3, 0, 2, 0]]
|
12
_sources/lib/index.rst.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
compLib
|
||||
#######
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 5
|
||||
|
||||
classes/Motor
|
||||
classes/Encoder
|
||||
classes/IRSensor
|
||||
classes/Seeding
|
||||
classes/DoubleElimination
|
||||
classes/Opencv
|
64
_sources/other/hardware.rst.txt
Normal file
|
@ -0,0 +1,64 @@
|
|||
.. _other_bardware:
|
||||
|
||||
Hardware
|
||||
########
|
||||
|
||||
Sensorarray
|
||||
***********
|
||||
|
||||
|SensorarrayImage|
|
||||
|
||||
.. |SensorarrayImage| image:: images/Sensorarray.png
|
||||
|
||||
Specs V4
|
||||
--------
|
||||
|
||||
| **Processor:** `STM32G030F6P6 <https://mou.sr/3UxW49B>`_ - 32-bit ARM Cortex M0 CPU @ 64 MHz
|
||||
| **I/O:** 1x I2C, 1x SWD
|
||||
| **Sensors:** 5x `QRE1113GR <https://mou.sr/3TWGYdI>`_
|
||||
|
||||
Specs V2
|
||||
--------
|
||||
|
||||
| **Processor:** `ATMEGA328P-AU <https://mou.sr/3FxhPC5>`_ - 8-bit CPU @ 16 MHz
|
||||
| **I/O:** 1x I2C, 1x UART, 1x ISP
|
||||
| **Sensors:** 5x `QRE1113GR <https://mou.sr/3TWGYdI>`_
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
Das Sensorarray wird verwendet um Linienen vor dem Roboter zu erkennen. Es agiert als I2C Slave und muss dementsprechend aktiv gepollt werden.
|
||||
Zusätzlich besteht die möglichkeit alle Emitter zu deaktiviern um einen eventuellen Messfehler durch Sonneneinstralung oder andere Störquellen zu erkennen.
|
||||
|
||||
Version 4 unterscheidet sich zu Version 2 im Mikroprozessor, da es zu Lieferengpässen des ATMEGA gekommen ist.
|
||||
Zusätzlich wurde die möglichkeit alle Emitter einzeln an bzw. auszuschalten entfernt, da diese keinen signifikanten Mehrwert brachte.
|
||||
|
||||
Motorboard
|
||||
**********
|
||||
|
||||
|MainboardImage|
|
||||
|
||||
.. |MainboardImage| image:: images/Mainboard.png
|
||||
|
||||
Specs
|
||||
-----
|
||||
**Motor-Treiber:** `LV8548MC-AH <https://mou.sr/3TXbFzu>`_
|
||||
|
||||
Details
|
||||
-------
|
||||
Das Motorboard kann an einen der 4 Ports am Roboter angesteckt werden und ermöglicht das Ansteuern von Motoren und auslesen von Encodern.
|
||||
|
||||
Mainboard
|
||||
*********
|
||||
|
||||
Specs
|
||||
-----
|
||||
|
||||
| **Processor:** `STM32L051C8T6TR <https://mou.sr/3fuaAQv>`_ - 32-bit ARM Cortex M0 @ 32MHz
|
||||
| **I/O:** 4x I2C (3x Bus 1, 1x Bus 2), 1x 40 Pin GPIO Header, 2x SPI (Verbunden mit GPIO), 4x Motor-/Servo-connector, 1x SWD, 1x USB-C
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
Das Mainboard wird auf den GPIO-Header eines Raspberry Pi gesteckt und ermöglicht die Steuerung eines Roboters mittels 4 Motor- bzw. Servo-Ports. Der RaspberryPi kommuniziert dabei mittels SPI mit dem Mainboard und steuert die einzelnen Sensoren oder Module an.
|
||||
Zusätzlich befinden sich auf der Unterseite des Mainboards Lötstellen, welche direkt mit der Stromversorgung der Motoren verbunden sind und geben so die möglichkeit Motoren mit mehr als 5V anzusteuern.
|
166
_sources/other/usage.rst.txt
Normal file
|
@ -0,0 +1,166 @@
|
|||
.. _other_usage:
|
||||
|
||||
Beispiele
|
||||
#########
|
||||
|
||||
Vorwärts und rückwärts fahren
|
||||
*****************************
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from compLib.Motor import *
|
||||
|
||||
def forward():
|
||||
Motor.power(0, -30)
|
||||
Motor.power(3, 30)
|
||||
|
||||
|
||||
def backward():
|
||||
Motor.power(0, 30)
|
||||
Motor.power(3, -30)
|
||||
|
||||
def main():
|
||||
print("hallo ich bin ein roboter beep buup")
|
||||
|
||||
forward()
|
||||
time.sleep(1)
|
||||
backward()
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
Eine Linie verfolgen
|
||||
********************
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
from compLib.Encoder import Encoder
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
COLOR_BREAK = 850
|
||||
DRIVE_SPEED = 35
|
||||
|
||||
IRSensor.enable()
|
||||
|
||||
def drive(left, right):
|
||||
right *= -1
|
||||
Motor.multiple_power((0, right), (3, left))
|
||||
print(f"{left} {right}")
|
||||
|
||||
def follow():
|
||||
while True:
|
||||
sensors = IRSensor.read_all()
|
||||
|
||||
if sensors[0] > COLOR_BREAK:
|
||||
# turn left
|
||||
drive(-DRIVE_SPEED, DRIVE_SPEED)
|
||||
elif sensors[4] > COLOR_BREAK:
|
||||
# turn right
|
||||
drive(DRIVE_SPEED, -DRIVE_SPEED)
|
||||
else:
|
||||
# straight
|
||||
drive(DRIVE_SPEED, DRIVE_SPEED)
|
||||
|
||||
if sensors[0] > COLOR_BREAK and sensors[4] > COLOR_BREAK:
|
||||
break
|
||||
|
||||
drive(0, 0)
|
||||
time.sleep(1)
|
||||
|
||||
def main():
|
||||
follow()
|
||||
|
||||
drive(DRIVE_SPEED, DRIVE_SPEED)
|
||||
time.sleep(0.5)
|
||||
follow()
|
||||
|
||||
drive(DRIVE_SPEED, DRIVE_SPEED)
|
||||
time.sleep(0.5)
|
||||
follow()
|
||||
|
||||
drive(DRIVE_SPEED, DRIVE_SPEED)
|
||||
time.sleep(0.5)
|
||||
follow()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Funktionalität des Roboters überprüfen
|
||||
**************************************
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from compLib.Motor import Motor
|
||||
from compLib.Encoder import Encoder
|
||||
from compLib.IRSensor import IRSensor
|
||||
|
||||
|
||||
def testIR():
|
||||
print("Enabling Infrared Sensor")
|
||||
IRSensor.enable()
|
||||
time.sleep(1)
|
||||
|
||||
print("Writing sensor values...")
|
||||
for i in range(0, 50):
|
||||
print(IRSensor.read_all())
|
||||
time.sleep(0.1)
|
||||
|
||||
print("Disabling Infrared Sensor")
|
||||
IRSensor.disable()
|
||||
|
||||
def testEncoders():
|
||||
Motor.multiple_pulse_width((0, 50), (3, -50))
|
||||
|
||||
print("Writing encoder positions...")
|
||||
for i in range(0, 50):
|
||||
print(Encoder.read_all_positions())
|
||||
time.sleep(0.1)
|
||||
|
||||
time.sleep(2)
|
||||
print("Writing encoder velocities...")
|
||||
for i in range(0, 50):
|
||||
print(Encoder.read_all_velocities())
|
||||
time.sleep(0.1)
|
||||
|
||||
Motor.multiple_pulse_width((0, 0), (3, 0))
|
||||
|
||||
|
||||
def testMotors():
|
||||
print("Setting pulse_with")
|
||||
Motor.multiple_pulse_width((0, 50), (3, -50))
|
||||
time.sleep(3)
|
||||
|
||||
print("Setting power")
|
||||
Motor.multiple_power((0, 50), (3, -50))
|
||||
time.sleep(3)
|
||||
|
||||
print("Setting pulse_with")
|
||||
Motor.multiple_speed((0, 5), (3, -5))
|
||||
time.sleep(3)
|
||||
|
||||
for i in range(0, 100):
|
||||
Motor.multiple_power((0, i), (3, -i))
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Make sure robot is turned on it's back!")
|
||||
time.sleep(5)
|
||||
|
||||
print()
|
||||
print("----------------- Testing Infrared Sensor -----------------")
|
||||
testIR()
|
||||
|
||||
print()
|
||||
print("----------------- Testing Encoder -----------------")
|
||||
testEncoders()
|
||||
|
||||
print()
|
||||
print("----------------- Testing Motors -----------------")
|
||||
testMotors()
|
59
_sources/software/installation.rst.txt
Normal file
|
@ -0,0 +1,59 @@
|
|||
.. _software_installation:
|
||||
|
||||
Installationsanweisungen
|
||||
########################
|
||||
|
||||
Diese Anleitung dient dazu die Software auf dem Roboter neu aufzusetzen.
|
||||
|
||||
**Im normalen Gebraucht sollte dies jedoch nicht notwendig sein.**
|
||||
|
||||
Betriebssystem aufsetzen
|
||||
========================
|
||||
|
||||
Als Basis wird für den Roboter Raspberry Pi OS (64-bit) verwendet. Das 32-Bit Betriebssystem wird nicht unterstützt, da die Software-Komponenten nur für aarch64 bzw. arm64/v8 kompiliert werden.
|
||||
Genauere Informationen sind `hier <https://www.raspberrypi.com/software/operating-systems/>`_ zu finden.
|
||||
|
||||
Bearbeiten der boot-Partition
|
||||
=============================
|
||||
|
||||
1. ``cmdline.txt``
|
||||
|
||||
::
|
||||
|
||||
console=tty1 root=PARTUUID=21e60f8c-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh
|
||||
|
||||
|
||||
Stellen Sie sicher, dass die folgenden Einstellungen in der ``config.txt`` korrekt gesetzt sind
|
||||
|
||||
2. ``config.txt``
|
||||
|
||||
::
|
||||
|
||||
# SPI
|
||||
dtparam=spi=on
|
||||
dtoverlay=spi1-3cs
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
[all]
|
||||
|
||||
[pi4]
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[all]
|
||||
start_x=1
|
||||
gpu_mem=128
|
||||
|
||||
dtoverlay=pi3-disable-bt
|
||||
enable_uart=1
|
||||
|
||||
3. Erstellen der leeren Datei ``ssh``, damit ssh beim nächsten Start aktiviert wird
|
||||
4. Hinzufügen der ``userconf.txt``
|
||||
|
||||
::
|
||||
|
||||
compair:$6$eh2pkHm18RgYtwiG$PoeabVCH8llbyIio66OefPGXZ2r2BRI2hPHIdkNTBjmiR0lGXsozGyLx0uViOx3bi998syXjSDXkwt0t3x8Bq.
|
||||
|
||||
5. Wlan Verbindung einrichten
|
134
_static/_sphinx_javascript_frameworks_compat.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* _sphinx_javascript_frameworks_compat.js
|
||||
* ~~~~~~~~~~
|
||||
*
|
||||
* Compatability shim for jQuery and underscores.js.
|
||||
*
|
||||
* WILL BE REMOVED IN Sphinx 6.0
|
||||
* xref RemovedInSphinx60Warning
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* select a different prefix for underscore
|
||||
*/
|
||||
$u = _.noConflict();
|
||||
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
if (!x) {
|
||||
return x
|
||||
}
|
||||
return decodeURIComponent(x.replace(/\+/g, ' '));
|
||||
};
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s === 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node, addItems) {
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.className = className;
|
||||
}
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
if (isInSVG) {
|
||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
var bbox = node.parentElement.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": node.parentNode,
|
||||
"target": rect});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
}
|
||||
}
|
||||
var addItems = [];
|
||||
var result = this.each(function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
for (var i = 0; i < addItems.length; ++i) {
|
||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
2506
_static/_stemmer.js
Normal file
294
_static/base-stemmer.js
Normal file
|
@ -0,0 +1,294 @@
|
|||
/**@constructor*/
|
||||
BaseStemmer = function() {
|
||||
this.setCurrent = function(value) {
|
||||
this.current = value;
|
||||
this.cursor = 0;
|
||||
this.limit = this.current.length;
|
||||
this.limit_backward = 0;
|
||||
this.bra = this.cursor;
|
||||
this.ket = this.limit;
|
||||
};
|
||||
|
||||
this.getCurrent = function() {
|
||||
return this.current;
|
||||
};
|
||||
|
||||
this.copy_from = function(other) {
|
||||
this.current = other.current;
|
||||
this.cursor = other.cursor;
|
||||
this.limit = other.limit;
|
||||
this.limit_backward = other.limit_backward;
|
||||
this.bra = other.bra;
|
||||
this.ket = other.ket;
|
||||
};
|
||||
|
||||
this.in_grouping = function(s, min, max) {
|
||||
if (this.cursor >= this.limit) return false;
|
||||
var ch = this.current.charCodeAt(this.cursor);
|
||||
if (ch > max || ch < min) return false;
|
||||
ch -= min;
|
||||
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
|
||||
this.cursor++;
|
||||
return true;
|
||||
};
|
||||
|
||||
this.in_grouping_b = function(s, min, max) {
|
||||
if (this.cursor <= this.limit_backward) return false;
|
||||
var ch = this.current.charCodeAt(this.cursor - 1);
|
||||
if (ch > max || ch < min) return false;
|
||||
ch -= min;
|
||||
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
|
||||
this.cursor--;
|
||||
return true;
|
||||
};
|
||||
|
||||
this.out_grouping = function(s, min, max) {
|
||||
if (this.cursor >= this.limit) return false;
|
||||
var ch = this.current.charCodeAt(this.cursor);
|
||||
if (ch > max || ch < min) {
|
||||
this.cursor++;
|
||||
return true;
|
||||
}
|
||||
ch -= min;
|
||||
if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
|
||||
this.cursor++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
this.out_grouping_b = function(s, min, max) {
|
||||
if (this.cursor <= this.limit_backward) return false;
|
||||
var ch = this.current.charCodeAt(this.cursor - 1);
|
||||
if (ch > max || ch < min) {
|
||||
this.cursor--;
|
||||
return true;
|
||||
}
|
||||
ch -= min;
|
||||
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
|
||||
this.cursor--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
this.eq_s = function(s)
|
||||
{
|
||||
if (this.limit - this.cursor < s.length) return false;
|
||||
if (this.current.slice(this.cursor, this.cursor + s.length) != s)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.cursor += s.length;
|
||||
return true;
|
||||
};
|
||||
|
||||
this.eq_s_b = function(s)
|
||||
{
|
||||
if (this.cursor - this.limit_backward < s.length) return false;
|
||||
if (this.current.slice(this.cursor - s.length, this.cursor) != s)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.cursor -= s.length;
|
||||
return true;
|
||||
};
|
||||
|
||||
/** @return {number} */ this.find_among = function(v)
|
||||
{
|
||||
var i = 0;
|
||||
var j = v.length;
|
||||
|
||||
var c = this.cursor;
|
||||
var l = this.limit;
|
||||
|
||||
var common_i = 0;
|
||||
var common_j = 0;
|
||||
|
||||
var first_key_inspected = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var k = i + ((j - i) >>> 1);
|
||||
var diff = 0;
|
||||
var common = common_i < common_j ? common_i : common_j; // smaller
|
||||
// w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
|
||||
var w = v[k];
|
||||
var i2;
|
||||
for (i2 = common; i2 < w[0].length; i2++)
|
||||
{
|
||||
if (c + common == l)
|
||||
{
|
||||
diff = -1;
|
||||
break;
|
||||
}
|
||||
diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
|
||||
if (diff != 0) break;
|
||||
common++;
|
||||
}
|
||||
if (diff < 0)
|
||||
{
|
||||
j = k;
|
||||
common_j = common;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = k;
|
||||
common_i = common;
|
||||
}
|
||||
if (j - i <= 1)
|
||||
{
|
||||
if (i > 0) break; // v->s has been inspected
|
||||
if (j == i) break; // only one item in v
|
||||
|
||||
// - but now we need to go round once more to get
|
||||
// v->s inspected. This looks messy, but is actually
|
||||
// the optimal approach.
|
||||
|
||||
if (first_key_inspected) break;
|
||||
first_key_inspected = true;
|
||||
}
|
||||
}
|
||||
do {
|
||||
var w = v[i];
|
||||
if (common_i >= w[0].length)
|
||||
{
|
||||
this.cursor = c + w[0].length;
|
||||
if (w.length < 4) return w[2];
|
||||
var res = w[3](this);
|
||||
this.cursor = c + w[0].length;
|
||||
if (res) return w[2];
|
||||
}
|
||||
i = w[1];
|
||||
} while (i >= 0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// find_among_b is for backwards processing. Same comments apply
|
||||
this.find_among_b = function(v)
|
||||
{
|
||||
var i = 0;
|
||||
var j = v.length
|
||||
|
||||
var c = this.cursor;
|
||||
var lb = this.limit_backward;
|
||||
|
||||
var common_i = 0;
|
||||
var common_j = 0;
|
||||
|
||||
var first_key_inspected = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var k = i + ((j - i) >> 1);
|
||||
var diff = 0;
|
||||
var common = common_i < common_j ? common_i : common_j;
|
||||
var w = v[k];
|
||||
var i2;
|
||||
for (i2 = w[0].length - 1 - common; i2 >= 0; i2--)
|
||||
{
|
||||
if (c - common == lb)
|
||||
{
|
||||
diff = -1;
|
||||
break;
|
||||
}
|
||||
diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
|
||||
if (diff != 0) break;
|
||||
common++;
|
||||
}
|
||||
if (diff < 0)
|
||||
{
|
||||
j = k;
|
||||
common_j = common;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = k;
|
||||
common_i = common;
|
||||
}
|
||||
if (j - i <= 1)
|
||||
{
|
||||
if (i > 0) break;
|
||||
if (j == i) break;
|
||||
if (first_key_inspected) break;
|
||||
first_key_inspected = true;
|
||||
}
|
||||
}
|
||||
do {
|
||||
var w = v[i];
|
||||
if (common_i >= w[0].length)
|
||||
{
|
||||
this.cursor = c - w[0].length;
|
||||
if (w.length < 4) return w[2];
|
||||
var res = w[3](this);
|
||||
this.cursor = c - w[0].length;
|
||||
if (res) return w[2];
|
||||
}
|
||||
i = w[1];
|
||||
} while (i >= 0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
/* to replace chars between c_bra and c_ket in this.current by the
|
||||
* chars in s.
|
||||
*/
|
||||
this.replace_s = function(c_bra, c_ket, s)
|
||||
{
|
||||
var adjustment = s.length - (c_ket - c_bra);
|
||||
this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
|
||||
this.limit += adjustment;
|
||||
if (this.cursor >= c_ket) this.cursor += adjustment;
|
||||
else if (this.cursor > c_bra) this.cursor = c_bra;
|
||||
return adjustment;
|
||||
};
|
||||
|
||||
this.slice_check = function()
|
||||
{
|
||||
if (this.bra < 0 ||
|
||||
this.bra > this.ket ||
|
||||
this.ket > this.limit ||
|
||||
this.limit > this.current.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
this.slice_from = function(s)
|
||||
{
|
||||
var result = false;
|
||||
if (this.slice_check())
|
||||
{
|
||||
this.replace_s(this.bra, this.ket, s);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
this.slice_del = function()
|
||||
{
|
||||
return this.slice_from("");
|
||||
};
|
||||
|
||||
this.insert = function(c_bra, c_ket, s)
|
||||
{
|
||||
var adjustment = this.replace_s(c_bra, c_ket, s);
|
||||
if (c_bra <= this.bra) this.bra += adjustment;
|
||||
if (c_bra <= this.ket) this.ket += adjustment;
|
||||
};
|
||||
|
||||
this.slice_to = function()
|
||||
{
|
||||
var result = '';
|
||||
if (this.slice_check())
|
||||
{
|
||||
result = this.current.slice(this.bra, this.ket);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
this.assign_to = function()
|
||||
{
|
||||
return this.current.slice(0, this.limit);
|
||||
};
|
||||
};
|
856
_static/basic.css
Normal file
|
@ -0,0 +1,856 @@
|
|||
/*
|
||||
* basic.css
|
||||
* ~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2021 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 div.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: 450px;
|
||||
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;
|
||||
}
|
||||
|
||||
a.brackets:before,
|
||||
span.brackets > a:before{
|
||||
content: "[";
|
||||
}
|
||||
|
||||
a.brackets:after,
|
||||
span.brackets > a:after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
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, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
img.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 {
|
||||
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;
|
||||
}
|
||||
|
||||
div.admonition, div.topic, blockquote {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
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,
|
||||
div.topic > :last-child,
|
||||
div.admonition > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sidebar::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;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
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 {
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.figure p.caption {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.figure p.caption 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;
|
||||
}
|
||||
|
||||
|
||||
/* -- 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;
|
||||
}
|
||||
|
||||
dl.footnote > dt,
|
||||
dl.citation > dt {
|
||||
float: left;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
dl.footnote > dd,
|
||||
dl.citation > dd {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl.footnote > dd:after,
|
||||
dl.citation > dd: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 > dt:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.sig-paren {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.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.5em;
|
||||
content: ":";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
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.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
code.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
189
_static/compair-logo-white.svg
Normal file
After Width: | Height: | Size: 188 KiB |
1
_static/css/badge_only.css
Normal file
|
@ -0,0 +1 @@
|
|||
.fa:before{-webkit-font-smoothing:antialiased}.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}}
|
BIN
_static/css/fonts/Roboto-Slab-Bold.woff
Normal file
BIN
_static/css/fonts/Roboto-Slab-Bold.woff2
Normal file
BIN
_static/css/fonts/Roboto-Slab-Regular.woff
Normal file
BIN
_static/css/fonts/Roboto-Slab-Regular.woff2
Normal file
BIN
_static/css/fonts/fontawesome-webfont.eot
Normal file
2671
_static/css/fonts/fontawesome-webfont.svg
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
_static/css/fonts/fontawesome-webfont.ttf
Normal file
BIN
_static/css/fonts/fontawesome-webfont.woff
Normal file
BIN
_static/css/fonts/fontawesome-webfont.woff2
Normal file
BIN
_static/css/fonts/lato-bold-italic.woff
Normal file
BIN
_static/css/fonts/lato-bold-italic.woff2
Normal file
BIN
_static/css/fonts/lato-bold.woff
Normal file
BIN
_static/css/fonts/lato-bold.woff2
Normal file
BIN
_static/css/fonts/lato-normal-italic.woff
Normal file
BIN
_static/css/fonts/lato-normal-italic.woff2
Normal file
BIN
_static/css/fonts/lato-normal.woff
Normal file
BIN
_static/css/fonts/lato-normal.woff2
Normal file
4
_static/css/theme.css
Normal file
316
_static/doctools.js
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* doctools.js
|
||||
* ~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* select a different prefix for underscore
|
||||
*/
|
||||
$u = _.noConflict();
|
||||
|
||||
/**
|
||||
* make the code below compatible with browsers without
|
||||
* an installed firebug like debugger
|
||||
if (!window.console || !console.firebug) {
|
||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
||||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
|
||||
"profile", "profileEnd"];
|
||||
window.console = {};
|
||||
for (var i = 0; i < names.length; ++i)
|
||||
window.console[names[i]] = function() {};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s === 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node, addItems) {
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.className = className;
|
||||
}
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
if (isInSVG) {
|
||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
var bbox = node.parentElement.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": node.parentNode,
|
||||
"target": rect});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
}
|
||||
}
|
||||
var addItems = [];
|
||||
var result = this.each(function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
for (var i = 0; i < addItems.length; ++i) {
|
||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
var Documentation = {
|
||||
|
||||
init : function() {
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
|
||||
this.initOnKeyListeners();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return 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 : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated === 'undefined')
|
||||
return string;
|
||||
return (typeof translated === 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated === 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
||||
addTranslations : function(catalog) {
|
||||
for (var key in catalog.messages)
|
||||
this.TRANSLATIONS[key] = catalog.messages[key];
|
||||
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
|
||||
this.LOCALE = catalog.locale;
|
||||
},
|
||||
|
||||
/**
|
||||
* add context elements like header anchor links
|
||||
*/
|
||||
addContextElements : function() {
|
||||
$('div[id] > :header:first').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this headline')).
|
||||
appendTo(this);
|
||||
});
|
||||
$('dt[id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this definition')).
|
||||
appendTo(this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash && $.browser.mozilla)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* highlight the search words provided in the url in the text
|
||||
*/
|
||||
highlightSearchWords : function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
|
||||
if (terms.length) {
|
||||
var body = $('div.body');
|
||||
if (!body.length) {
|
||||
body = $('body');
|
||||
}
|
||||
window.setTimeout(function() {
|
||||
$.each(terms, function() {
|
||||
body.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
}, 10);
|
||||
$('<p class="highlight-link"><a href="javascript:Documentation.' +
|
||||
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
|
||||
.appendTo($('#searchbox'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the domain index toggle buttons
|
||||
*/
|
||||
initIndexTable : function() {
|
||||
var togglers = $('img.toggler').click(function() {
|
||||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) === 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
}).css('display', '');
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
|
||||
togglers.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords : function() {
|
||||
$('#searchbox .highlight-link').fadeOut(300);
|
||||
$('span.highlighted').removeClass('highlighted');
|
||||
},
|
||||
|
||||
/**
|
||||
* make the url absolute
|
||||
*/
|
||||
makeURL : function(relativeURL) {
|
||||
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the current relative url
|
||||
*/
|
||||
getCurrentURL : function() {
|
||||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this === '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
},
|
||||
|
||||
initOnKeyListeners: function() {
|
||||
$(document).keydown(function(event) {
|
||||
var activeElementType = document.activeElement.tagName;
|
||||
// don't navigate when in search box, textarea, dropdown or button
|
||||
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
|
||||
&& activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey
|
||||
&& !event.shiftKey) {
|
||||
switch (event.keyCode) {
|
||||
case 37: // left
|
||||
var prevHref = $('link[rel="prev"]').prop('href');
|
||||
if (prevHref) {
|
||||
window.location.href = prevHref;
|
||||
return false;
|
||||
}
|
||||
case 39: // right
|
||||
var nextHref = $('link[rel="next"]').prop('href');
|
||||
if (nextHref) {
|
||||
window.location.href = nextHref;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// quick alias for translations
|
||||
_ = Documentation.gettext;
|
||||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
12
_static/documentation_options.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.2.3',
|
||||
LANGUAGE: 'de',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
FILE_SUFFIX: '.html',
|
||||
LINK_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false
|
||||
};
|
BIN
_static/file.png
Normal file
After Width: | Height: | Size: 286 B |