diff --git a/Dockerfile b/Dockerfile index 5e038a26e767d98314eea4ca6e0167cc0c2728be..92fea904a7efac71d89c042ed833c2a140bee726 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,20 @@ -FROM python:3.5.6-stretch +FROM python:3.8 AS builder -ARG REPO_USER -ARG REPO_PASSWORD -ARG DOWNLOAD_URL=https://${REPO_USER}:${REPO_PASSWORD}@update.fischertechnik-cloud.com/repository/ft-txt-lib/simple/ +ARG TWINE_USERNAME +ARG TWINE_PASSWORD -RUN apt-get update && apt-get install -y apache2 apache2-dev python-smbus +ENV TWINE_USERNAME=$TWINE_USERNAME +ENV TWINE_PASSWORD=$TWINE_PASSWORD -ADD apache/default.conf /etc/apache2/sites-enabled/000-default.conf -ADD apache/controller.crt /etc/ssl/crt/controller.crt -ADD apache/controller.key /etc/ssl/crt/controller.key +WORKDIR /usr/src/app -ADD ./txtapi /var/www/txtapi -RUN mkdir -p /var/www/txtapi/workspace +RUN pip install --no-cache-dir twine -RUN chown -R www-data:www-data /var/www/txtapi +ADD . /usr/src/app -RUN a2enmod ssl proxy proxy_http +RUN cd /usr/src/app/txtapi && \ + ./publish.sh -RUN pip3 install --extra-index-url $DOWNLOAD_URL txtapi numpy opencv-python -WORKDIR /var/www/txtapi -COPY entrypoint.sh ./ -CMD ./entrypoint.sh diff --git a/DockerfilePublish b/DockerfilePublish deleted file mode 100644 index 92fea904a7efac71d89c042ed833c2a140bee726..0000000000000000000000000000000000000000 --- a/DockerfilePublish +++ /dev/null @@ -1,20 +0,0 @@ -FROM python:3.8 AS builder - -ARG TWINE_USERNAME -ARG TWINE_PASSWORD - -ENV TWINE_USERNAME=$TWINE_USERNAME -ENV TWINE_PASSWORD=$TWINE_PASSWORD - -WORKDIR /usr/src/app - -RUN pip install --no-cache-dir twine - -ADD . /usr/src/app - -RUN cd /usr/src/app/txtapi && \ - ./publish.sh - - - - diff --git a/README.md b/README.md index 0a7c1b17079ee6f63505855e163eb0e1e109bd2b..3db4feb80f29f0e10896d3921839c9ce18fd2cbe 100644 --- a/README.md +++ b/README.md @@ -19,33 +19,6 @@ Konfiguriere einen Git-Hook, sodass die Versionsnummern automatisch inkrementier > git config --local core.hooksPath .githooks/ -## Docker -Start der Api: -```code -docker-compose up --build -``` -Danach erreich unter **http://localhost/api/v1/ui/** - -## Community FW -Vorbereitung: -1. Auf dem eigenen Rechner müssen Python3 und pip vorhanden sein -2. Per pip fabric installieren (pip install fabric) -3. Auf dem TXT muss die Community Firmware installiert sein und das WLAN eingerichet sein - -Ablauf: -1. fabric deploy -h 'ip-adresse' -* Auf dem Controller muss der SSH und root Zugriff der Tastendruck bestätigt werden -* An einer Stelle wird nach Credentials gefragt, hier bitte die Accountdaten für update.fischertechnik-cloud.com eingeben -2. Im Menü des Controller die Applikation TXT-API ausführen -3. Sobald der Status auf 'running' steht, kann der Controller genutzt werdne -4. ROBOPro2 öffnen (nicht die Webapplikation) -* [https://update.fischertechnik-cloud.com/#browse/browse:ft-roboticstudio] -5. Über das kleine Router Symbol kann der Controller verbunden werden -* Im Dialogfeld die Ip-Adresse des Controllers und den Port 8080 eingeben (Beispiel: 192.168.1.2:8080) -* Der Port 8080 ist aktuell noch notwendig, da in der Community Firmware automatisch ein Server startet, der Port 80 allokiert -6. Wenn alles funktioniert hat, sollte der Router nun grün sein (rot im Fehlerfall) -7. Über den Start- und den Stopknopf kann nun Code ausgeführt und beendet werden. - ## Release durchführen * Prüfen ob der letzte Stand ohne Änderungen lokal vorhanden ist diff --git a/apache/controller.crt b/apache/controller.crt deleted file mode 100644 index b5477411ebc3e9cc630410e3bee6eef1804673ee..0000000000000000000000000000000000000000 --- a/apache/controller.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDeDCCAmCgAwIBAgIJAKH0imK5rYHkMA0GCSqGSIb3DQEBCwUAMFExCzAJBgNV -BAYTAkRFMQwwCgYDVQQIDANOUlcxETAPBgNVBAcMCERvcnRtdW5kMSEwHwYDVQQK -DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTkxMDA5MTE0MTE0WhcNMjAx -MDA4MTE0MTE0WjBRMQswCQYDVQQGEwJERTEMMAoGA1UECAwDTlJXMREwDwYDVQQH -DAhEb3J0bXVuZDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwGTI5U+BiY0fbIFCyvHsIC2E -4R/TmaUCAZ4EA1l9VeSNzhlmR/F2vsSrnQtyHBqeTrHM4k06QO4Pbb5/eXYot/Im -A7UuFmxNzYVj/AGCLjLXPxdQcSCuBgaUdDYDF0gjppY/TBuYt2Uizrb9U0HJIo+u -RoiIqz2qk2L9VPE1X6PeGDUdUdqnlvf4xj/YWKtrh83AhY8E5uBnIjNj7sy/t3sL -li8k31p+/sa0QQCE/JPW3z/VyZDoIBTPCDTYr2mTvZGeXw+qFgY48+SsVwmWQOlg -J0O5xh+nRqWRN6BXj7uDSDMwBk/NVAezK8o8brZJELEVha4avCQ7eWi4lmwMFwID -AQABo1MwUTAdBgNVHQ4EFgQUSabsfhwSDoHTRRrJXnWGzEil5F8wHwYDVR0jBBgw -FoAUSabsfhwSDoHTRRrJXnWGzEil5F8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG -9w0BAQsFAAOCAQEAb8J1q1bKoXeUiX2bVEmoNMNEI4MfKOEiSWvkaCie3w814TWN -VFfVRBEr8PdiOC2lNgOOBag4CwSJYTYvZLyZEf+Yca6xgkMc0BwP9wW4Y72/r9X0 -sRQjk1/4nMpnFZap7VW+kJHLjRHYTS367RFxAKVyLwKDE4E1ZN2DabfE1GAM09za -BvrDXi5qRA/ZF6fepaaHMagIRDJ+p14eibCgFAY+09ivDAM7jW3yEa1z36zfCjQZ -Z7c6hpi2nal93eI6CuebQLsiR6tNkVvrYi/fDFXLkyP+QrcvKLdDLFHWm37XsZt4 -MltWDFtNUg52wULK2IUG0ffr/uAf18mc7lZcZQ== ------END CERTIFICATE----- diff --git a/apache/controller.key b/apache/controller.key deleted file mode 100644 index beb127ffbb1391ad68c08ce01585dc3eb9223923..0000000000000000000000000000000000000000 --- a/apache/controller.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAZMjlT4GJjR9s -gULK8ewgLYThH9OZpQIBngQDWX1V5I3OGWZH8Xa+xKudC3IcGp5OscziTTpA7g9t -vn95dii38iYDtS4WbE3NhWP8AYIuMtc/F1BxIK4GBpR0NgMXSCOmlj9MG5i3ZSLO -tv1TQckij65GiIirPaqTYv1U8TVfo94YNR1R2qeW9/jGP9hYq2uHzcCFjwTm4Gci -M2PuzL+3ewuWLyTfWn7+xrRBAIT8k9bfP9XJkOggFM8INNivaZO9kZ5fD6oWBjjz -5KxXCZZA6WAnQ7nGH6dGpZE3oFePu4NIMzAGT81UB7MryjxutkkQsRWFrhq8JDt5 -aLiWbAwXAgMBAAECggEAcUonnhgOIDAwg9UtvpFsSJplN6dlE4E9yDQDCvHkQvK9 -qdH9D3oZQUZQA77cp73o4FAEZHGzTs6f2TCmoaA5Y++8AMzsYZnOyqm0cY112bxo -USdqX+MtdjDjs/amJUx+abbpeOh7Kers7yqDy0XeqXJP3grB54v9aKKOqDkNA85W -IG480T8HaOmwKBOYqI6cZT3g14PsxR6jVbmPJ0aSwrB41IuPYkRYpnq8iKESlvDe -4MMaLv/5MKKQcPyRp0R9NsIDgiXndHv48p/NKuNd2bDM93DUyoxriMZI+ji5V1Kr -65ao9kLhLX8pBl5RZU5FsgAvl3Ci6eM8JnPVbMlvMQKBgQDj9f03YOadt12OSi5a -yLHVZ8pWZOboYt8koBtHdRtLlsGIitKDvnWQltQSjQuCXFe2fWnryQegiEpv/xnR -FsBk1X2Wn15ScpP7GuAqoCKj3AstQGweUpIVf3l6XvCFcwIkFrkw6PBH87wU8w33 -W+PovMwq3+WyNJqj86BcxXmQ2QKBgQDYDtvf0KzDFf+m5ifXUKDUBlo8SUoajvRT -5+iRinVRtDdV/rjbmUwUKoLC5u0Z+fIRw5U3W03BJO/PqIu5kMd0w5yHkLeMJeUC -A8KQEK2/Fr0opq7E0QG7Kgj3MZ6pj21EzYQ8OzSvObmYHyxyXufSq6UrcqEMZBKo -E18wcqFubwKBgGLfSZGgZMYZRum1QP+9NmySFM99+izm3VPcYZiPsWQKoixf0ci0 -bfdlg9v78vb6qEyNfsh2q+kz091Zrs/iZ1YUxuDyhI2MBqUN1haG2B9sDCj3XS2V -sKjEXmL2FKo85LvUY0RUdAsxKu2HMhKMd4B1irQ54j111XCw9Wfner+ZAoGBAMhr -GCkI0IlzCaBPVVvVDis/7UqJDbWPMDP5JvKDBosQ6lfaHq6OCFWYjY2wWvbCtXsm -+27LQlhNJCt0BJDRLUQyBCx44NqfeEyjIkMzRYjB1hw0amBmJP3yYziyjaRqSIkP -P4ADx75XhMMI/9jkEpWI5YFlJuFwnyHMPnuZigf/AoGAQ41H+ZPYo5LP+H2nlBxo -8hkp9SidlvPevmTvTqWJSEzmy2p4OzxHrWsr7/Jl5YqCW8CxPr6gl8kizotN3V9p -0/y0jJ6KCs5M8+O/mm6jVkey2sle78Fnv5IOWDo0CkOJ3kRKJBzV80iHxKTGCJkK -IlfDd465De99tYAOzQuiVBY= ------END PRIVATE KEY----- diff --git a/apache/default.conf b/apache/default.conf deleted file mode 100644 index 418909b280233fb21f80e7dd13b4406d986e520d..0000000000000000000000000000000000000000 --- a/apache/default.conf +++ /dev/null @@ -1,10 +0,0 @@ -<VirtualHost *:80> - ErrorLog /dev/stdout - LogLevel info - <Proxy *> - Order allow,deny - Allow from all - </Proxy> - ProxyPass /api http://localhost:8080/api - ProxyPassReverse /api http://localhost:8080/api -</VirtualHost> \ No newline at end of file diff --git a/community-fw/README.md b/community-fw/README.md deleted file mode 100644 index 30b31fd942313617bdcdaeccbf95451476ce43ea..0000000000000000000000000000000000000000 --- a/community-fw/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Fischertechnik TXT - -Dieses Projekt dient dem vollständig automatischen Einrichten des Fischertechnik-TXT für die Entwicklung des RoboticStudio. Dazu wird ein SSH-Zugriff ohne Login auf den TXT eingerichtet, entsprechende Berechtigungen hinterlegt, Python installiert und der eigentliche Quellcode auf dem Server hinterlegt. Das Projekt gliedert sich im Wesentlichen in 3 Ordner: - -- deploy: Hier sind die Skripte zum Deployment auf dem TXT untergebracht. -- flaskApp: Hier befindet sich die grafische Oberfläche, mit der der Swagger-Server auf dem TXT gesteuert werden kann. -- flaskConnnexion: Hier liegt der eigentliche Swagger-Server. - -Üblicherweise wird beim ersten Einrichten des TXT einmal das komplette Deployment durchgeführt; nach einem Neustart lediglich der Task `mount`. Wird am Swagger-Server entwickelt, so kann mit `deploy_flask_app` der Python-Code der grafischen Oberfläche und des Swagger-Servers aktualisiert werden. - -## Vorraussetzungen -Auf dem Rechner, von dem aus deployt werden soll, muss `python3` installiert sein. Zusätzlich wird die Python-Bibliothek `fabric` (https://github.com/fabric/fabric) benötigt. Darüber hinaus muss der TXT im selben WLAN sein und die Fischertechnik-CommunityEdition gebootet sein. - -## Ausführen des Skriptes -Ist die Bibliothek `fabric` lokal installiert, steht der Command `fab` zum Ausführen der Tasks aus der sogenannten Fabfile zur Verfügung. Diese beinhaltet fünf verschiedene Tasks zum Deployment, die im folgenden erklärt werden. Es können alle Tasks mit dem Befehl `fab --list` angezeigt werden - -### Komplettes Deployment durchführen: deploy -Der auszuführende Task lautet: -```bash -fab deploy -h '<ip>' -``` -Dieser ist ein Sammeltask und führt die folgenden 4 Tasks nacheinander aus. Das Skript beinhaltet einige Print-Ausgaben und Anweisungen, sodass der Prozess des Einrichtens auf der lokalen Kommandozeile dokumentiert wird. - -### SSH-Key auf den Server kopieren: copy_ssh_key -Der auszuführende Task lautet: -```bash -fab copy_ssh_key -h '<ip>' -``` -Damit die Python-Skripte auf dem Controller ausgeführt werden können, muss der Public-SSH-Key des Rechners, von dem deployt werden soll, auf dem TXT in der Datei `.ssh/authorized_keys` liegen. Ist dies nicht der Fall werden je nach eigener SSH-Konfiguration unterschiedliche Fehler geworfen und das Deployment läuft **nicht** durch. Das Kopieren des SSH-Keys übernimmt das Skript automatisch; es lädt die Datei `.ssh/id_rsa.pub` und kopiert diese mittels SCP auf den Server in die entsprechende Datei. Hierzu ist zweimal das Erlauben des SSH-Logins über die Nutzeroberfläche des TXT notwendig. - -### Prüfen der Mounts: mount -Der auszuführende Task lautet: -```bash -fab mount -h '<ip>' -``` -Damit die Python Skripte korrekt ausgeführt werden können, müssen die Mount-Points richtig initialisiert werden. Das Skript prüft automatisch, ob die Mounts in der `/etc/mtab` eingetragen sind und wenn nicht versucht es diese hinzuzufügen. Dazu muss auf dem Display zweimal der Root-Zugriff bestätigt werden. Stellt das Skript darüber hinaus fest, dass die Mount-Points noch nicht einmal initialisiert wurden, erstellt es die Verzeichnisse unter `~/mounts` und kopiert die Dateien in diese. Eine Besonderheit stellt dabei die Datei `/usr/bin/sudo`, also der Sudo-Command, dar. Diese Datei muss den Eigentümer root und das setuid-Bit muss gesetzt sein. Um diesen Schritt kümmert sich das Skript ebenfalls automatisiert, jedoch ist hier zusätzlich zweimal der Root-Zugriff auf dem Display zu bestätigen. - -### Prüfen von Pip und den benötigten Dependencies: install_python -Der auszuführende Task lautet: -```bash -fab install_python -h '<ip>' -``` -Damit alle Anforderungen für den Flask-Server vorhanden sind, werden alle Dependencies auf dem TXT geprüft. Im ersten Schritt wird geprüft, ob pip installiert ist. Ist dies nicht der Fall, wird pip auf dem TXT installiert. Danach werden mittels des Commands `pip list --format=freeze` einmal alle installierten Bibliotheken ausgegeben und mit der lokalen Datei `flaskConnexion/requirements.txt` abgeglichen. Fehlen Bibliotheken, werden diese nachinstalliert. Dies bedeutet auch, dass nach einer Anpassung der lokalen Requirements die neuen Bibliotheken auch auf dem TXT installiert werden. - -### Hochladen des Flask-Servers: deploy_flask_app -Der auszuführende Task lautet: -```bash -fab deploy_flask_app -h '<ip>' -``` -Zuletzt muss noch der Quellcode auf den TXT geladen werden. Hierzu werden diw Order `flaskConnexion` (Flask-Server) und `flaskApp` (GUI-App) mithilfe von tar zu einem Archiv gepackt, dann auf den TXT hochgeladen und wieder entpackt. Die GUI-App wird derart angepasst, dass diese ausführbar ist. Zusätzlich wird die Oberfläche des TXT neu geladen, damit die neue App erscheint. Anschließend wird die IP in der `swagger.yml` auf die aktuelle IP des TXT gesetzt. - -## Verwendung der Flask-App - -Um die App zu Starten geht man auf dem TXT in den Ordner Demos und startet die Anwendung `Flask`. Die Anwendung läuft in zwei separaten Prozessen; einer für die grafische Oberfläche und ein zweiter für den Swagger-Server. Die grafische Oberfläche hat eine Status-Anzeige sowie jeweils einen Start- und Stop-Button. Mit dem Start Button wird der Swagger-Server gestartet; dies dauert ca. 25 Sekunden. Danach kann die grafische Oberfläche verlassen werden; der Swagger-Server läuft im Hintergrund weiter. Die Swagger-UI ist unter http://ip-of-my-txt:8080/v1/ui erreichbar; die API selbst unter steht unter http://ip-of-my-txt:8080/v1 zur Verfügung. Mit dem Stop-Button wird der Swagger-Server beendet. \ No newline at end of file diff --git a/community-fw/api-app/icon.png b/community-fw/api-app/icon.png deleted file mode 100755 index 3b3a38a4bfb2f011b6345ee2963517cc6e8f1e3b..0000000000000000000000000000000000000000 Binary files a/community-fw/api-app/icon.png and /dev/null differ diff --git a/community-fw/api-app/manifest b/community-fw/api-app/manifest deleted file mode 100755 index 3b1e3e18dad8cd409cd699ba4a7650d3e726b60f..0000000000000000000000000000000000000000 --- a/community-fw/api-app/manifest +++ /dev/null @@ -1,11 +0,0 @@ -[app] -name: TXT-API -category: IDE -author: Beemo -icon: icon.png -desc: TXT-API for fischertechnik -exec: start.py -managed: yes -uuid: 191fe5a6-313b-4083-af65-d1ad7fd6d281 -version: 0.1 -firmware: 0.9 \ No newline at end of file diff --git a/community-fw/api-app/refresh.py b/community-fw/api-app/refresh.py deleted file mode 100755 index 787e9d3be9c2db23e97ae6fc30cdeefcefea9b0c..0000000000000000000000000000000000000000 --- a/community-fw/api-app/refresh.py +++ /dev/null @@ -1,10 +0,0 @@ -import socket - -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -try: - print("Requesting rescan ...") - # Connect to server and send data - sock.connect(("localhost", 9000)) - sock.sendall(bytes("rescan\n", "UTF-8")) -except socket.error as msg: - print("Unable to connect to Launcher: ", msg) \ No newline at end of file diff --git a/community-fw/api-app/start.py b/community-fw/api-app/start.py deleted file mode 100755 index a41778e64a194933f6619dc862172a3caf6e3b90..0000000000000000000000000000000000000000 --- a/community-fw/api-app/start.py +++ /dev/null @@ -1,78 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -import glob -import os -import signal -import sys -import time - -from TouchStyle import * - -class FtcGuiApplication(TouchApplication): - def __init__(self, args): - TouchApplication.__init__(self, args) - - self.process = "txtapi" - - # Creates an empty MainWindow - w = TouchWindow("TXT-API") - - # Create a label - vbox = QVBoxLayout() - - status = QLabel() - status.setText('Server status:') - status.setAlignment(Qt.AlignCenter) - vbox.addWidget(status) - - self.info = QLabel() - self.info.setAlignment(Qt.AlignCenter) - vbox.addWidget(self.info) - - self.btnStart = QPushButton('Start') - self.btnStart.clicked.connect(self.startBackgroundServer) - vbox.addWidget(self.btnStart) - - self.btnStop = QPushButton('Stop') - self.btnStop.clicked.connect(self.stopBackgroundServer) - vbox.addWidget(self.btnStop) - - w.centralWidget.setLayout(vbox) - - self.checkServerStatus() - - w.show() - self.exec_() - - def initBackgroundServer(self): - self.info.setText('booting') - self.info.setStyleSheet('color: orange; background-color: white') - self.btnStart.setDisabled(True) - self.processEvents() - - def startBackgroundServer(self): - self.initBackgroundServer() - os.system("cd /home/ftc/txtapi && {0} &".format(self.process)) - time.sleep(25) - self.checkServerStatus() - - def stopBackgroundServer(self): - os.system("killall -9 {0}".format(self.process)) - self.checkServerStatus() - - def checkServerStatus(self): - processes = os.popen("ps -Af").read() - if self.process in processes: - self.info.setText('running') - self.info.setStyleSheet('color: green; background-color: white') - self.btnStart.setDisabled(True) - self.btnStop.setDisabled(False) - else: - self.info.setText('not running') - self.info.setStyleSheet('color: red; background-color: white') - self.btnStart.setDisabled(False) - self.btnStop.setDisabled(True) - -if __name__ == "__main__": - FtcGuiApplication(sys.argv) diff --git a/community-fw/deploy/colorized_output.py b/community-fw/deploy/colorized_output.py deleted file mode 100755 index cb1305c65172553a34aa9ad4539158cfaf027e88..0000000000000000000000000000000000000000 --- a/community-fw/deploy/colorized_output.py +++ /dev/null @@ -1,27 +0,0 @@ -class Color: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - -def print_colored(string, color): - print(color + string + Color.ENDC) - -def print_bold(string): - print_colored(string, Color.BOLD) - -def print_warning(string): - print_colored(string, Color.WARNING) - -def print_success(string): - print_colored(string, Color.OKGREEN) - -def print_info(string): - print_colored(string, Color.OKBLUE) - -def print_error(string): - print_colored(string, Color.FAIL) \ No newline at end of file diff --git a/community-fw/deploy/deployment.py b/community-fw/deploy/deployment.py deleted file mode 100644 index b7e371c717c039186d2c6e98eda836652555ac59..0000000000000000000000000000000000000000 --- a/community-fw/deploy/deployment.py +++ /dev/null @@ -1,36 +0,0 @@ -import os - -from deploy import colorized_output as co -from deploy.mounts import Mounts -from deploy.python_environment import PythonEnvironment -from deploy.repository import Repository - -from fabric import Connection - -USER = 'ftc' - -class Deployment: - def __init__(self, host): - self.host = host - self.con = Connection(host = host, user = USER) - - def copy_ssh_key(self): - co.print_warning('Please authorize ssh-login on txt to copy ssh-key to authorized keys') - os.system("ssh {0}@{1} 'mkdir -p .ssh' && scp $HOME/.ssh/id_rsa.pub {0}@{1}:.ssh/authorized_keys".format(USER, self.host)) - - def check_mounts(self): - mnts = Mounts(self.con) - mnts.check_mounts() - - def check_pip_and_libraries(self): - pyenv = PythonEnvironment(self.con) - pyenv.check_pip() - pyenv.check_pip_libraries() - pyenv.add_ftc_path() - - def deploy_flask_app(self): - repo = Repository(self.con) - repo.uploadFolder('../txtapi') - repo.uploadAppFolder('api-app') - repo.replace_ip(self.host) - repo.replace_env('environment_cfw.py') diff --git a/community-fw/deploy/mounts.py b/community-fw/deploy/mounts.py deleted file mode 100755 index 0b962939a384a545635f928336984f06a9eea378..0000000000000000000000000000000000000000 --- a/community-fw/deploy/mounts.py +++ /dev/null @@ -1,37 +0,0 @@ -from deploy import colorized_output as co - -class Mounts: - MOUNTED_SUDO = '/home/ftc/mounts/usr/bin/sudo' - - def __init__(self, connection): - self.connection = connection - self.mounts = self.connection.run('cat /etc/mtab').stdout.strip().split() - - def create_mounts(self): - co.print_info('Copying files to local mount folders') - self.copy_folder_to_mounts('/usr/lib/python3.7/.', '~/mounts/python3.7') - self.copy_folder_to_mounts('/usr/bin', '~/mounts/usr') - self.chown_mounted_sudo() - - def copy_folder_to_mounts(self, src, dst): - self.connection.run("mkdir -p {0}".format(dst)) - self.connection.run("cp -r {0} {1}".format(src, dst)) - - def chown_mounted_sudo(self): - self.connection.sudo("chown root:root {0}".format(Mounts.MOUNTED_SUDO)) - self.connection.sudo("chmod 4755 {0}".format(Mounts.MOUNTED_SUDO)) - - def check_mounts(self): - if not self.connection.run('ls mounts', warn=True): - self.create_mounts() - self.check_mount('/usr/lib/python3.7', '/home/ftc/mounts/python3.7') - self.check_mount('/usr/bin', '/home/ftc/mounts/usr/bin') - - def check_mount(self, mnt, mnt_dir): - co.print_info("Checking mount {0}".format(mnt)) - if mnt in self.mounts: - co.print_success("--> Already mounted") - else: - co.print_warning("--> Not mounted. Please authorize sudo on txt to mount it") - self.connection.sudo("mount {0} {1}".format(mnt_dir, mnt)) - co.print_success("--> Sucessfully mounted") \ No newline at end of file diff --git a/community-fw/deploy/python_environment.py b/community-fw/deploy/python_environment.py deleted file mode 100755 index 683a54493d0cdd08003d405ef3b371ae44deb812..0000000000000000000000000000000000000000 --- a/community-fw/deploy/python_environment.py +++ /dev/null @@ -1,54 +0,0 @@ -from deploy import colorized_output as co - -class PythonEnvironment: - def __init__(self, connection): - self.connection = connection - - def add_ftc_path(self): - co.print_info('Add /opt/ftc to PYTHONPATH') - self.connection.run('echo "export PYTHONPATH=/opt/ftc" > /home/ftc/.profile', warn=True) - co.print_success('--> Important for ui applications') - - def check_pip(self): - co.print_info("Checking pip-installation") - if self.connection.run('ls /usr/bin | grep pip3.7', warn=True): - co.print_success('--> Already installed') - else: - co.print_warning('--> Not installed. Starting installation of pip') - self.install_pip('~/tmp') - co.print_success('--> Successfully installed pip') - - def install_pip(self, tmp_dir): - self.connection.run("mkdir -p {0}".format(tmp_dir)) - self.connection.run("wget https://bootstrap.pypa.io/get-pip.py -O/home/ftc/tmp/get-pip.py".format(tmp_dir)) - self.connection.run("python {0}/get-pip.py".format(tmp_dir)) - self.connection.run("rm -rf {0}".format(tmp_dir)) - - def check_pip_libraries(self): - co.print_info('Checking pip libraries') - requirements = self.get_requirements() - pip_list = self.get_pip_list() - for requirement in requirements: - search = requirement.strip().replace(' ', '') - self.check_pip_library(pip_list, search) - - def check_pip_library(self, dependencies, dependency): - co.print_info("Checking {0}:".format(dependency)) - if dependency in dependencies: - co.print_success("--> Already installed") - else: - co.print_warning("--> Not installed. Starting installation") - if dependency.find('ft-') == -1 and dependency.find('ftrobopy') == -1 and dependency.find('txtapi') == -1: - self.connection.run("pip install {0}".format(dependency)) - else: - co.print_info("Downloading from custom repository") - co.print_info("Enter credentials if needed") - self.connection.run("pip install --index-url https://update.fischertechnik-cloud.com/repository/ft-txt-lib/simple/ --no-deps {0}".format(dependency)) - co.print_success("--> Successfully installed") - - def get_pip_list(self): - return self.connection.run('pip list --format=freeze').stdout.strip().split() - - def get_requirements(self): - fd = open('../txtapi/requirements.txt', 'r') - return fd.readlines() diff --git a/community-fw/deploy/repository.py b/community-fw/deploy/repository.py deleted file mode 100755 index 632c2e8bfb47745bbf7af39bb5bf9f5fadbfc791..0000000000000000000000000000000000000000 --- a/community-fw/deploy/repository.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -from deploy import colorized_output as co - -class Repository: - def __init__(self, connection): - self.connection = connection - - def uploadFolder(self, folder): - tar_folder = "{0}.tar".format(folder.split('/')[-1]) - os.system("tar -cf {0} {1}/".format(tar_folder, folder)) - self.connection.put("{0}".format(tar_folder)) - self.connection.run("tar -xf {0}".format(tar_folder)) - self.connection.run("rm {0}".format(tar_folder)) - os.system("rm {0}".format(tar_folder)) - - def uploadAppFolder(self, folder): - self.uploadFolder(folder) - co.print_info('UploadAppFolder ' + folder) - self.connection.run("mkdir -p apps", warn=True) - self.connection.run("rm -rf apps/{0}/".format(folder), warn=True) - self.connection.run("mv -f {0} apps/".format(folder)) - self.connection.run("chmod +x apps/{0}/start.py".format(folder)) - self.connection.run("python apps/{0}/refresh.py".format(folder)) - - def replace_ip(self, ip): - self.connection.run("cd txtapi/txtapi/openapi && sed -i 's/^host:.*$/host: \"{0}:8080\"/g' openapi.yaml".format(ip)) - - def replace_env(self, env_file): - self.connection.run("mv txtapi/txtapi/environment/{0} txtapi/txtapi/environment/environment.py".format(env_file)) \ No newline at end of file diff --git a/community-fw/fabfile.py b/community-fw/fabfile.py deleted file mode 100755 index 23551bf3f4780adc47cef306a8b5c88bc6069c98..0000000000000000000000000000000000000000 --- a/community-fw/fabfile.py +++ /dev/null @@ -1,57 +0,0 @@ -import os - -from deploy import colorized_output as co -from deploy.deployment import Deployment - -from invoke import task - -def set_ctx(ctx, host): - deployment = Deployment(host) - ctx.update({'deploy': deployment}) - -@task(help={'host': "IP of the txt-controller"}) -def copy_ssh_key(ctx, host): - """ - copy the own ssh-key to the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.copy_ssh_key() - -@task(help={'host': "IP of the txt-controller"}) -def mount(ctx, host): - """ - mounts the needed directories on the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.check_mounts() - -@task(help={'host': "IP of the txt-controller"}) -def install_python(ctx, host): - """ - installs pip and dependencies on the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.check_pip_and_libraries() - -@task(help={'host': "IP of the txt-controller"}) -def deploy_flask_app(ctx, host): - """ - deploys the flask-app on the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.deploy_flask_app() - -@task(help={'host': "IP of the txt-controller"}) -def deploy(ctx, host): - """ - Setup and deploy flask-server on txt-controller - """ - set_ctx(ctx, host) - co.print_info("Step 1/4 >> Copying SSH-Key to TXT") - copy_ssh_key(ctx, host) - co.print_info("Step 2/4 >> Creating mounts") - mount(ctx, host) - co.print_info("Step 3/4 >> Installing pip and dependencies") - install_python(ctx, host) - co.print_success('Deployment successfully finished') - co.print_success("You can start the swagger server by running the 'TXT-API' application") diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6073fd7af8d055da9af5e110a7e7319e5fcd7f29..0000000000000000000000000000000000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: '3' -services: - api: - build: - context: . - args: - # Needs to be url encoded - REPO_USER: 'repo-download' - REPO_PASSWORD: 'SieheEnpass' - ports: - - 80:80 - - 443:443 - - 8080:8080 diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index c007968f485f52618236dc9211f65c8cb497d46d..0000000000000000000000000000000000000000 --- a/entrypoint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -apache2ctl -D BACKGROUND - -txtapi - diff --git a/openapi.json b/openapi.json index 74f00c18f8b5ad7ceda8471b1012c67cc464abc4..79084c19365942249443b02acf2be8da92f1dbcf 100644 --- a/openapi.json +++ b/openapi.json @@ -385,7 +385,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/file" + "$ref": "#/components/schemas/controller_file" } } } @@ -491,7 +491,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/file" + "$ref": "#/components/schemas/controller_file" } } } @@ -3176,7 +3176,8 @@ "ApiKeyAuth": [] } ], - "description": "send voicecommand", + "summary": "DEPRICATED - Add a text to the running program", + "description": "DEPRICATED - Adds a text to the running program on the controller.", "operationId": "send_command", "parameters": [ { @@ -3211,17 +3212,169 @@ }, "x-openapi-router-controller": "txtapi.controllers.remote_controller" } + }, + "/remote/voice": { + "post": { + "tags": [ + "remote" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "summary": "Add a text event to the running program", + "description": "Adds a text event to the running program on the controller.", + "operationId": "add_voice_event", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/voice_event" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "412": { + "$ref": "#/components/responses/PreConditionFailed" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "default": { + "$ref": "#/components/responses/defaultError" + } + }, + "x-openapi-router-controller": "txtapi.controllers.remote_controller" + } + }, + "/remote": { + "post": { + "tags": [ + "remote" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "summary": "Add a event to the running program", + "description": "Adds a event to the running program on the controller.", + "operationId": "add_remote_event", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/remote_event" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "412": { + "$ref": "#/components/responses/PreConditionFailed" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "default": { + "$ref": "#/components/responses/defaultError" + } + }, + "x-openapi-router-controller": "txtapi.controllers.remote_controller" + } + }, + "/remote/stream": { + "get": { + "tags": [ + "remote" + ], + "security": [ + { + "ApiKeyQueryAuth": [] + } + ], + "summary": "Event stream for remote control", + "description": "Message stream for events from running programs on the controller.", + "operationId": "remote_event_stream", + "parameters": [ + { + "in": "query", + "name": "X-API-KEY", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/event-stream": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "default": { + "$ref": "#/components/responses/defaultError" + } + }, + "x-openapi-router-controller": "txtapi.controllers.remote_controller" + } } }, "servers": [ { - "url": "http://localhost/api/v1" + "url": "http://localhost/api/v1", + "description": "HTTP" + }, + { + "url": "http://txt40.local/api/v1", + "description": "Local" + }, + { + "url": "http://192.168.7.2/api/v1", + "description": "USB" }, { - "url": "https://localhost/api/v1" + "url": "http://192.168.8.2/api/v1", + "description": "WLAN-AP" }, { - "url": "http://192.168.7.2/api/v1" + "url": "http://192.168.9.2/api/v1", + "description": "Bluetooth" } ], "components": { @@ -3319,7 +3472,7 @@ }, "x-body-name": "workspace" }, - "file": { + "controller_file": { "type": "object", "properties": { "name": { @@ -3327,9 +3480,12 @@ }, "path": { "type": "string" + }, + "file_content": { + "type": "string" } }, - "x-body-name": "file" + "x-body-name": "controller_file" }, "inline_response_default": { "type": "object", @@ -3825,6 +3981,42 @@ } }, "x-body-name": "controller" + }, + "voice_event": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "x-body-name": "voice_event" + }, + "remote_event": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "attributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/attribute" + } + } + }, + "x-body-name": "remote_event" + }, + "attribute": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "x-body-name": "attribute" } } } diff --git a/start-dev.sh b/start-dev.sh deleted file mode 100755 index 5456d10c69ef2551ee5f6b76b6454c1bfc92c59e..0000000000000000000000000000000000000000 --- a/start-dev.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# stop instances -docker-compose down - -# rebuild images and containers -docker-compose up --build -d \ No newline at end of file diff --git a/txt2/api-app/icon.png b/txt2/api-app/icon.png deleted file mode 100755 index 3b3a38a4bfb2f011b6345ee2963517cc6e8f1e3b..0000000000000000000000000000000000000000 Binary files a/txt2/api-app/icon.png and /dev/null differ diff --git a/txt2/api-app/manifest b/txt2/api-app/manifest deleted file mode 100755 index 3b1e3e18dad8cd409cd699ba4a7650d3e726b60f..0000000000000000000000000000000000000000 --- a/txt2/api-app/manifest +++ /dev/null @@ -1,11 +0,0 @@ -[app] -name: TXT-API -category: IDE -author: Beemo -icon: icon.png -desc: TXT-API for fischertechnik -exec: start.py -managed: yes -uuid: 191fe5a6-313b-4083-af65-d1ad7fd6d281 -version: 0.1 -firmware: 0.9 \ No newline at end of file diff --git a/txt2/api-app/refresh.py b/txt2/api-app/refresh.py deleted file mode 100755 index 787e9d3be9c2db23e97ae6fc30cdeefcefea9b0c..0000000000000000000000000000000000000000 --- a/txt2/api-app/refresh.py +++ /dev/null @@ -1,10 +0,0 @@ -import socket - -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -try: - print("Requesting rescan ...") - # Connect to server and send data - sock.connect(("localhost", 9000)) - sock.sendall(bytes("rescan\n", "UTF-8")) -except socket.error as msg: - print("Unable to connect to Launcher: ", msg) \ No newline at end of file diff --git a/txt2/api-app/start.py b/txt2/api-app/start.py deleted file mode 100755 index a41778e64a194933f6619dc862172a3caf6e3b90..0000000000000000000000000000000000000000 --- a/txt2/api-app/start.py +++ /dev/null @@ -1,78 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -import glob -import os -import signal -import sys -import time - -from TouchStyle import * - -class FtcGuiApplication(TouchApplication): - def __init__(self, args): - TouchApplication.__init__(self, args) - - self.process = "txtapi" - - # Creates an empty MainWindow - w = TouchWindow("TXT-API") - - # Create a label - vbox = QVBoxLayout() - - status = QLabel() - status.setText('Server status:') - status.setAlignment(Qt.AlignCenter) - vbox.addWidget(status) - - self.info = QLabel() - self.info.setAlignment(Qt.AlignCenter) - vbox.addWidget(self.info) - - self.btnStart = QPushButton('Start') - self.btnStart.clicked.connect(self.startBackgroundServer) - vbox.addWidget(self.btnStart) - - self.btnStop = QPushButton('Stop') - self.btnStop.clicked.connect(self.stopBackgroundServer) - vbox.addWidget(self.btnStop) - - w.centralWidget.setLayout(vbox) - - self.checkServerStatus() - - w.show() - self.exec_() - - def initBackgroundServer(self): - self.info.setText('booting') - self.info.setStyleSheet('color: orange; background-color: white') - self.btnStart.setDisabled(True) - self.processEvents() - - def startBackgroundServer(self): - self.initBackgroundServer() - os.system("cd /home/ftc/txtapi && {0} &".format(self.process)) - time.sleep(25) - self.checkServerStatus() - - def stopBackgroundServer(self): - os.system("killall -9 {0}".format(self.process)) - self.checkServerStatus() - - def checkServerStatus(self): - processes = os.popen("ps -Af").read() - if self.process in processes: - self.info.setText('running') - self.info.setStyleSheet('color: green; background-color: white') - self.btnStart.setDisabled(True) - self.btnStop.setDisabled(False) - else: - self.info.setText('not running') - self.info.setStyleSheet('color: red; background-color: white') - self.btnStart.setDisabled(False) - self.btnStop.setDisabled(True) - -if __name__ == "__main__": - FtcGuiApplication(sys.argv) diff --git a/txt2/deploy/colorized_output.py b/txt2/deploy/colorized_output.py deleted file mode 100755 index cb1305c65172553a34aa9ad4539158cfaf027e88..0000000000000000000000000000000000000000 --- a/txt2/deploy/colorized_output.py +++ /dev/null @@ -1,27 +0,0 @@ -class Color: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - -def print_colored(string, color): - print(color + string + Color.ENDC) - -def print_bold(string): - print_colored(string, Color.BOLD) - -def print_warning(string): - print_colored(string, Color.WARNING) - -def print_success(string): - print_colored(string, Color.OKGREEN) - -def print_info(string): - print_colored(string, Color.OKBLUE) - -def print_error(string): - print_colored(string, Color.FAIL) \ No newline at end of file diff --git a/txt2/deploy/deployment.py b/txt2/deploy/deployment.py deleted file mode 100644 index ca74f18c2c53281e6854520d3c0e67ac1906b04a..0000000000000000000000000000000000000000 --- a/txt2/deploy/deployment.py +++ /dev/null @@ -1,24 +0,0 @@ -import os - -from deploy import colorized_output as co -from deploy.repository import Repository - -from fabric import Connection - -USER = 'root' - -class Deployment: - def __init__(self, host): - self.host = host - self.con = Connection(host = host, user = USER) - - def copy_ssh_key(self): - co.print_warning('Please authorize ssh-login on txt to copy ssh-key to authorized keys') - os.system("ssh {0}@{1} 'mkdir -p .ssh' && scp $HOME/.ssh/id_rsa.pub {0}@{1}:.ssh/authorized_keys".format(USER, self.host)) - - def deploy_flask_app(self): - repo = Repository(self.con) - repo.uploadFolder('../txtapi') - repo.uploadAppFolder('api-app') - repo.replace_ip(self.host) - repo.replace_env('environment_txt2.py') diff --git a/txt2/deploy/repository.py b/txt2/deploy/repository.py deleted file mode 100755 index 06500aab62db56f195ddc36c7f62ffd4f06abe73..0000000000000000000000000000000000000000 --- a/txt2/deploy/repository.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -from deploy import colorized_output as co - -class Repository: - def __init__(self, connection): - self.connection = connection - - def uploadFolder(self, folder): - tar_folder = "{0}.tar".format(folder.split('/')[-1]) - os.system("tar -cf {0} {1}/".format(tar_folder, folder)) - self.connection.put("{0}".format(tar_folder)) - self.connection.run("tar -xf {0}".format(tar_folder)) - self.connection.run("rm {0}".format(tar_folder)) - os.system("rm {0}".format(tar_folder)) - - def uploadAppFolder(self, folder): - self.uploadFolder(folder) - co.print_info('UploadAppFolder ' + folder) - self.connection.run("mkdir -p apps", warn=True) - self.connection.run("rm -rf apps/{0}/".format(folder), warn=True) - self.connection.run("mv -f {0} apps/".format(folder)) - self.connection.run("chmod +x apps/{0}/start.py".format(folder)) - - def replace_ip(self, ip): - self.connection.run("cd txtapi/txtapi/openapi && sed -i 's/^host:.*$/host: \"{0}:8080\"/g' openapi.yaml".format(ip)) - - def replace_env(self, env_file): - self.connection.run("mv txtapi/txtapi/environment/{0} txtapi/txtapi/environment/environment.py".format(env_file)) \ No newline at end of file diff --git a/txt2/fabfile.py b/txt2/fabfile.py deleted file mode 100755 index d79e4528fbac4be7e3cef495adfa3e9dcc91de2d..0000000000000000000000000000000000000000 --- a/txt2/fabfile.py +++ /dev/null @@ -1,39 +0,0 @@ -import os - -from deploy import colorized_output as co -from deploy.deployment import Deployment - -from invoke import task - -def set_ctx(ctx, host): - deployment = Deployment(host) - ctx.update({'deploy': deployment}) - -@task(help={'host': "IP of the txt-controller"}) -def copy_ssh_key(ctx, host): - """ - copy the own ssh-key to the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.copy_ssh_key() - -@task(help={'host': "IP of the txt-controller"}) -def deploy_flask_app(ctx, host): - """ - deploys the flask-app on the txt-controller - """ - set_ctx(ctx, host) - ctx.deploy.deploy_flask_app() - -@task(help={'host': "IP of the txt-controller"}) -def deploy(ctx, host): - """ - Setup and deploy flask-server on txt-controller - """ - set_ctx(ctx, host) - co.print_info("Step 1/2 >> Copying SSH-Key to TXT") - copy_ssh_key(ctx, host) - co.print_info("Step 2/2 >> Copying flaskConnexion-folder to TXT") - deploy_flask_app(ctx, host) - co.print_success('Deployment successfully finished') - co.print_success("You can start the swagger server by running the 'TXT-API' application") diff --git a/txtapi/.openapi-generator/FILES b/txtapi/.openapi-generator/FILES index f97d545d2c114f43f48d83b2cbbacf5d6676319f..19e394ce11847eaea11b7e4d5c2fa6ca612e606c 100644 --- a/txtapi/.openapi-generator/FILES +++ b/txtapi/.openapi-generator/FILES @@ -7,19 +7,20 @@ tox.ini txtapi/controllers/__init__.py txtapi/encoder.py txtapi/models/__init__.py +txtapi/models/attribute.py txtapi/models/ball_detector.py txtapi/models/base_model_.py txtapi/models/breakpoint.py txtapi/models/camera_config.py txtapi/models/color_detector.py txtapi/models/controller.py +txtapi/models/controller_file.py txtapi/models/counter.py txtapi/models/debugger_arguments.py txtapi/models/debugger_response.py txtapi/models/enabled.py txtapi/models/error_message.py txtapi/models/expression.py -txtapi/models/file.py txtapi/models/image_recognition_config.py txtapi/models/inline_response_default.py txtapi/models/input.py @@ -30,7 +31,9 @@ txtapi/models/motor_all_of.py txtapi/models/output.py txtapi/models/program_location.py txtapi/models/rectangle.py +txtapi/models/remote_event.py txtapi/models/servomotor.py +txtapi/models/voice_event.py txtapi/models/workspace.py txtapi/openapi/openapi.yaml txtapi/test/__init__.py diff --git a/txtapi/requirements.txt b/txtapi/requirements.txt index cf17cb088d8d5773c6d776666a5f8bf9425d23e2..da027c37324b7705d06ea646caec3ae42d44ddfb 100644 --- a/txtapi/requirements.txt +++ b/txtapi/requirements.txt @@ -4,10 +4,7 @@ python_dateutil==2.6.0 setuptools==41.4.0 Flask-Cors==3.0.6 rpdb==0.1.6 -ftrobopy==1.92 -bme680==1.0.5 -ft-controllerlib==1.0.42.0 -imutils==0.5.3 +ft-controllerlib==6.2.1 Werkzeug==0.16.1 waitress>=1.4.4 psutil==5.7.3 diff --git a/txtapi/setup.py b/txtapi/setup.py index 9b6e14502e5b5e2b3e38cfa76e1e723789e3f235..e0ca07727c5680ab3bbc71d324d62f1908032429 100644 --- a/txtapi/setup.py +++ b/txtapi/setup.py @@ -20,16 +20,11 @@ REQUIRES = [ "Flask-Cors==3.0.6", "rpdb==0.1.6", "websockets==7.0", - "ftrobopy==1.92", - "bme680==1.0.5", - "ft-controllerlib==6.0.8", + "ft-controllerlib==6.2.1", "jsonschema==3.2.0", - "imutils==0.5.3", "Werkzeug==0.16.1", - "smbus2>=0.2.1", "waitress>=1.4.4", "psutil==5.7.3", - "apds9960==0.2" ] setup( diff --git a/txtapi/txtapi/cache.py b/txtapi/txtapi/cache.py index 05ad11259ae2bb4a6581c17492b0c280b0c35312..07dfffeef22f72ebe858d4e18179de67bc75a110 100644 --- a/txtapi/txtapi/cache.py +++ b/txtapi/txtapi/cache.py @@ -21,7 +21,7 @@ class CachedSpecification(Specification): cache = pickle.load(f) if cache['md5_hash'] == md5_hash: return cache['spec'] - except OSError: + except: pass rv = cls._real_from_file(spec, arguments=arguments) @@ -32,8 +32,8 @@ class CachedSpecification(Specification): 'spec': rv } pickle.dump(cache, f) - except OSError as e: - print('Could not store spec in cache: %s', e) + except: + print('Could not store spec in cache') return rv diff --git a/txtapi/txtapi/controllers/remote_controller.py b/txtapi/txtapi/controllers/remote_controller.py index 3d928bf3e4b23bd30975119476650dbd432d2f1e..059a75b0c2d14915916501acdffb9975b6741e4a 100644 --- a/txtapi/txtapi/controllers/remote_controller.py +++ b/txtapi/txtapi/controllers/remote_controller.py @@ -1,20 +1,63 @@ import connexion import six - +from flask import Response from txtapi.models.error_message import ErrorMessage # noqa: E501 -from txtapi.models.inline_response_default import InlineResponseDefault # noqa: E501 -from txtapi import util +from txtapi.models.voice_event import VoiceEvent # noqa: E501 from txtapi.handlers import remote_handler +from txtapi import util + + +def add_remote_event(remote_event): # noqa: E501 + """Add a event to the running program + + Adds a event to the running program on the controller. # noqa: E501 + + :param remote_event: + :type remote_event: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + remote_event = connexion.request.get_json() # noqa: E501 + return remote_handler.add_remote_event(remote_event) + + +def add_voice_event(voice_event): # noqa: E501 + """Add a text event to the running program + + Adds a text event to the running program on the controller. # noqa: E501 + + :param voice_event: + :type voice_event: dict | bytes + + :rtype: None + """ + if connexion.request.is_json: + voice_event = VoiceEvent.from_dict(connexion.request.get_json()) # noqa: E501 + return remote_handler.add_voice_event(voice_event.text) + + +def remote_event_stream(x_api_key=None): # noqa: E501 + """Event stream for remote control + + Message stream for events from running programs on the controller. # noqa: E501 + + :param x_api_key: + :type x_api_key: str + + :rtype: str + """ + return Response(remote_handler.remote_event_stream(), mimetype='text/event-stream') def send_command(command): # noqa: E501 - """send_command + """DEPRICATED - Add a text to the running program - send voicecommand # noqa: E501 + DEPRICATED - Adds a text to the running program on the controller. # noqa: E501 :param command: The voicecommand to be executed :type command: str :rtype: None """ - return remote_handler.send_command(command) + return remote_handler.add_voice_event(command) diff --git a/txtapi/txtapi/controllers/util_controller.py b/txtapi/txtapi/controllers/util_controller.py index 9347a9dbdb2c700d011cae492e9a6b899bf15c91..8b8d3961b326a3bae43b4d03580c13c40a4fa030 100644 --- a/txtapi/txtapi/controllers/util_controller.py +++ b/txtapi/txtapi/controllers/util_controller.py @@ -10,7 +10,7 @@ def ping(): # noqa: E501 :rtype: str """ - return jsonify(util_handler.ping()) + return util_handler.ping() def stop(): # noqa: E501 """stop diff --git a/txtapi/txtapi/handlers/remote_handler.py b/txtapi/txtapi/handlers/remote_handler.py index 23120c6adab00a5fa45527ee2298000da9e88b16..8ca7cafd29b0737064483ef445e276187ff47882 100644 --- a/txtapi/txtapi/handlers/remote_handler.py +++ b/txtapi/txtapi/handlers/remote_handler.py @@ -1,16 +1,66 @@ +import json +import time import socket + from http import HTTPStatus from fischertechnik.control.VoiceControl import VoiceControl +from fischertechnik.control.RemoteControl import RemoteControl + + +def add_voice_event(event): -def send_command(command): - status_code = HTTPStatus.OK try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((VoiceControl.HOST, VoiceControl.PORT)) - s.send(command.encode('utf-8')) + s.send(event.encode('utf-8')) + except Exception as e: + pass + finally: + s.close() + + return None, HTTPStatus.OK + + +def add_remote_event(event): + + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((RemoteControl.HOST, RemoteControl.IN_PORT)) + message = json.dumps(event) + s.send(message.encode('utf-8')) except Exception as e: - status_code = HTTPStatus.NOT_FOUND + pass finally: s.close() - return None, status_code + return None, HTTPStatus.OK + + +def remote_event_stream(): + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + while True: + try: + s.connect((RemoteControl.HOST, RemoteControl.OUT_PORT)) + break + except Exception as e: + time.sleep(1) + + running = True + while running: + + try: + + data = s.recv(RemoteControl.BUFFERSIZE) + + if (data != b''): + + yield 'data: %s\n\n' % str(data.decode()) + + except Exception as e: + running = False + + print("command stream closed") + + return None, HTTPStatus.OK diff --git a/txtapi/txtapi/handlers/util_handler.py b/txtapi/txtapi/handlers/util_handler.py index 0c54a49fd365f9a2623ab228d71810d0d56a7495..9f7d19298ff5a6872605e99065683d02516bf2ba 100644 --- a/txtapi/txtapi/handlers/util_handler.py +++ b/txtapi/txtapi/handlers/util_handler.py @@ -1,3 +1,4 @@ +import subprocess from http import HTTPStatus from txtapi.handlers import application_handler @@ -8,9 +9,21 @@ from txtapi.utility.heartbeat.heartbeat import Heartbeat def ping(): Heartbeat.get_instance().keep_alive() - return None, HTTPStatus.OK + role = get_controller_role() + if role == 'Master' or role == 'Single': + return None, HTTPStatus.OK + return 'Controller is in extension mode', HTTPStatus.CONFLICT def stop(): application_handler.stop_applications() controller_handler.stop_controller() debugger_handler.stop_debuggers() + +def get_controller_role(): + output = str(subprocess.Popen(['ftconfig', 'dump'], stdout=subprocess.PIPE, + stderr=subprocess.STDOUT).communicate()[0]).replace('\\n', '') + start = output.find('name ') + 5 + end = output.find('sn', start) + return output[start:end] + + diff --git a/txtapi/txtapi/handlers/workspace_handler.py b/txtapi/txtapi/handlers/workspace_handler.py index 79c008c5bcca640e28ccace9d5e0f68ad75ac3a7..f32a02418ea4af350837bb42e1e830528bb25ed7 100644 --- a/txtapi/txtapi/handlers/workspace_handler.py +++ b/txtapi/txtapi/handlers/workspace_handler.py @@ -7,7 +7,7 @@ from http import HTTPStatus from flask import jsonify from txtapi.environment.environment import MANIFEST_FILENAME, PID_FILENAME, PROJECT_FILENAME from txtapi.environment.environment import WORKSPACE_BASEPATH, DEBUG -from txtapi.models.file import File +from txtapi.models.controller_file import ControllerFile from txtapi.models.workspace import Workspace from txtapi.txt.txt_state import TxtState @@ -23,6 +23,8 @@ def create_workspace(workspace_name): if DEBUG: print("Creating directory %s failed due to %s" % (path, str(exception))) return exception, HTTPStatus.INTERNAL_SERVER_ERROR + else: + delete_workspace(workspace_name) return build_workspace_response(workspace_name), HTTPStatus.CREATED @@ -78,7 +80,10 @@ def delete_workspace(workspace_name): def get_file_of_workspace(workspace_name, filename): path = os.path.join(WORKSPACE_BASEPATH, workspace_name, filename) if os.path.exists(path): - return jsonify(File(filename, path)), HTTPStatus.OK + fd = open(path, 'r') + content = fd.read() + fd.close() + return jsonify(ControllerFile(filename, path, content)), HTTPStatus.OK else: return None, HTTPStatus.NOT_FOUND @@ -89,10 +94,33 @@ def get_files_of_workspace(workspace_name): return None, HTTPStatus.NOT_FOUND files = [] for file in os.listdir(path): - files.append(File(file, os.path.join(path, file))) + file_path = os.path.join(path, file) + if os.path.isdir(file_path): + if os.path.basename(file_path) != '__pycache__': + files.extend(_get_files_of_dir(file_path)) + else: + fd = open(os.path.join(path, file), 'r') + content = fd.read() + fd.close() + files.append(ControllerFile(file, os.path.join(path, file), content)) return files, HTTPStatus.OK +def _get_files_of_dir(path): + files = [] + for file in os.listdir(path): + file_path = os.path.join(path, file) + if os.path.isdir(file_path): + if os.path.basename(file_path) != '__pycache__': + files.extend(_get_files_of_dir(file_path)) + else: + fd = open(os.path.join(path, file), 'r') + content = fd.read() + fd.close() + files.append(ControllerFile(file, os.path.join(path, file), content)) + return files + + def upload_files_to_workspace(workspace_name, files): if ftlock.try_lock(workspace_name + '.py') == 0: ftlock.notify(TxtState.TRANSFERRING.value) @@ -105,13 +133,13 @@ def upload_files_to_workspace(workspace_name, files): fileName = splitPath[-1] path = '/'.join(splitPath[:-1]) file_path = os.path.join(WORKSPACE_BASEPATH, workspace_name, path) - + if not os.path.exists(file_path): try: os.makedirs(file_path) except Exception as exception: return exception, HTTPStatus.INTERNAL_SERVER_ERROR - + file_path = os.path.join(file_path, fileName) file.save(file_path) if ftlock.try_lock(workspace_name + '.py') == 0: @@ -146,6 +174,7 @@ def get_pid(workspace_name: str) -> str: raise Exception("unable to find process with workspace_name " + workspace_name) return pid + def build_workspace_response(name): uuid = None try: @@ -153,7 +182,7 @@ def build_workspace_response(name): project_dict = json.load(project_file) if 'uuid' in project_dict: uuid = project_dict['uuid'] - except: + except: pass path = os.path.join(WORKSPACE_BASEPATH, name) return Workspace(name, path, uuid) diff --git a/txtapi/txtapi/models/__init__.py b/txtapi/txtapi/models/__init__.py index 9ef10283af282f4a2d45cfa8ac9a6114c8904a56..c4050b13d0d707deb7d444ced804f28e84a5e554 100644 --- a/txtapi/txtapi/models/__init__.py +++ b/txtapi/txtapi/models/__init__.py @@ -3,18 +3,19 @@ # flake8: noqa from __future__ import absolute_import # import models into model package +from txtapi.models.attribute import Attribute from txtapi.models.ball_detector import BallDetector from txtapi.models.breakpoint import Breakpoint from txtapi.models.camera_config import CameraConfig from txtapi.models.color_detector import ColorDetector from txtapi.models.controller import Controller +from txtapi.models.controller_file import ControllerFile from txtapi.models.counter import Counter from txtapi.models.debugger_arguments import DebuggerArguments from txtapi.models.debugger_response import DebuggerResponse from txtapi.models.enabled import Enabled from txtapi.models.error_message import ErrorMessage from txtapi.models.expression import Expression -from txtapi.models.file import File from txtapi.models.image_recognition_config import ImageRecognitionConfig from txtapi.models.inline_response_default import InlineResponseDefault from txtapi.models.input import Input @@ -25,5 +26,7 @@ from txtapi.models.motor_all_of import MotorAllOf from txtapi.models.output import Output from txtapi.models.program_location import ProgramLocation from txtapi.models.rectangle import Rectangle +from txtapi.models.remote_event import RemoteEvent from txtapi.models.servomotor import Servomotor +from txtapi.models.voice_event import VoiceEvent from txtapi.models.workspace import Workspace diff --git a/txtapi/txtapi/models/attribute.py b/txtapi/txtapi/models/attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..dd1862d81d46194cab7bb5f42585d8160d12207e --- /dev/null +++ b/txtapi/txtapi/models/attribute.py @@ -0,0 +1,90 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from txtapi.models.base_model_ import Model +from txtapi import util + + +class Attribute(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, name=None, value=None): # noqa: E501 + """Attribute - a model defined in OpenAPI + + :param name: The name of this Attribute. # noqa: E501 + :type name: str + :param value: The value of this Attribute. # noqa: E501 + :type value: str + """ + self.openapi_types = { + 'name': str, + 'value': str + } + + self.attribute_map = { + 'name': 'name', + 'value': 'value' + } + + self._name = name + self._value = value + + @classmethod + def from_dict(cls, dikt) -> 'Attribute': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The attribute of this Attribute. # noqa: E501 + :rtype: Attribute + """ + return util.deserialize_model(dikt, cls) + + @property + def name(self): + """Gets the name of this Attribute. + + + :return: The name of this Attribute. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this Attribute. + + + :param name: The name of this Attribute. + :type name: str + """ + + self._name = name + + @property + def value(self): + """Gets the value of this Attribute. + + + :return: The value of this Attribute. + :rtype: str + """ + return self._value + + @value.setter + def value(self, value): + """Sets the value of this Attribute. + + + :param value: The value of this Attribute. + :type value: str + """ + + self._value = value diff --git a/txtapi/txtapi/models/controller_file.py b/txtapi/txtapi/models/controller_file.py new file mode 100644 index 0000000000000000000000000000000000000000..1dd8c6ec7b26a2a4893dd7002553f332052edcf8 --- /dev/null +++ b/txtapi/txtapi/models/controller_file.py @@ -0,0 +1,116 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from txtapi.models.base_model_ import Model +from txtapi import util + + +class ControllerFile(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, name=None, path=None, file_content=None): # noqa: E501 + """ControllerFile - a model defined in OpenAPI + + :param name: The name of this ControllerFile. # noqa: E501 + :type name: str + :param path: The path of this ControllerFile. # noqa: E501 + :type path: str + :param file_content: The file_content of this ControllerFile. # noqa: E501 + :type file_content: str + """ + self.openapi_types = { + 'name': str, + 'path': str, + 'file_content': str + } + + self.attribute_map = { + 'name': 'name', + 'path': 'path', + 'file_content': 'file_content' + } + + self._name = name + self._path = path + self._file_content = file_content + + @classmethod + def from_dict(cls, dikt) -> 'ControllerFile': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The controller_file of this ControllerFile. # noqa: E501 + :rtype: ControllerFile + """ + return util.deserialize_model(dikt, cls) + + @property + def name(self): + """Gets the name of this ControllerFile. + + + :return: The name of this ControllerFile. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this ControllerFile. + + + :param name: The name of this ControllerFile. + :type name: str + """ + + self._name = name + + @property + def path(self): + """Gets the path of this ControllerFile. + + + :return: The path of this ControllerFile. + :rtype: str + """ + return self._path + + @path.setter + def path(self, path): + """Sets the path of this ControllerFile. + + + :param path: The path of this ControllerFile. + :type path: str + """ + + self._path = path + + @property + def file_content(self): + """Gets the file_content of this ControllerFile. + + + :return: The file_content of this ControllerFile. + :rtype: str + """ + return self._file_content + + @file_content.setter + def file_content(self, file_content): + """Sets the file_content of this ControllerFile. + + + :param file_content: The file_content of this ControllerFile. + :type file_content: str + """ + + self._file_content = file_content diff --git a/txtapi/txtapi/models/file.py b/txtapi/txtapi/models/file.py deleted file mode 100644 index 7025cee4de4baec407c541b969f0ab060a9c5ba9..0000000000000000000000000000000000000000 --- a/txtapi/txtapi/models/file.py +++ /dev/null @@ -1,90 +0,0 @@ -# coding: utf-8 - -from __future__ import absolute_import -from datetime import date, datetime # noqa: F401 - -from typing import List, Dict # noqa: F401 - -from txtapi.models.base_model_ import Model -from txtapi import util - - -class File(Model): - """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - - Do not edit the class manually. - """ - - def __init__(self, name=None, path=None): # noqa: E501 - """File - a model defined in OpenAPI - - :param name: The name of this File. # noqa: E501 - :type name: str - :param path: The path of this File. # noqa: E501 - :type path: str - """ - self.openapi_types = { - 'name': str, - 'path': str - } - - self.attribute_map = { - 'name': 'name', - 'path': 'path' - } - - self._name = name - self._path = path - - @classmethod - def from_dict(cls, dikt) -> 'File': - """Returns the dict as a model - - :param dikt: A dict. - :type: dict - :return: The file of this File. # noqa: E501 - :rtype: File - """ - return util.deserialize_model(dikt, cls) - - @property - def name(self): - """Gets the name of this File. - - - :return: The name of this File. - :rtype: str - """ - return self._name - - @name.setter - def name(self, name): - """Sets the name of this File. - - - :param name: The name of this File. - :type name: str - """ - - self._name = name - - @property - def path(self): - """Gets the path of this File. - - - :return: The path of this File. - :rtype: str - """ - return self._path - - @path.setter - def path(self, path): - """Sets the path of this File. - - - :param path: The path of this File. - :type path: str - """ - - self._path = path diff --git a/txtapi/txtapi/models/remote_event.py b/txtapi/txtapi/models/remote_event.py new file mode 100644 index 0000000000000000000000000000000000000000..9d43bd9f31df88e64454fa7705e8ab718c4a5b2b --- /dev/null +++ b/txtapi/txtapi/models/remote_event.py @@ -0,0 +1,92 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from txtapi.models.base_model_ import Model +from txtapi.models.attribute import Attribute +from txtapi import util + +from txtapi.models.attribute import Attribute # noqa: E501 + +class RemoteEvent(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id=None, attributes=None): # noqa: E501 + """RemoteEvent - a model defined in OpenAPI + + :param id: The id of this RemoteEvent. # noqa: E501 + :type id: str + :param attributes: The attributes of this RemoteEvent. # noqa: E501 + :type attributes: List[Attribute] + """ + self.openapi_types = { + 'id': str, + 'attributes': List[Attribute] + } + + self.attribute_map = { + 'id': 'id', + 'attributes': 'attributes' + } + + self._id = id + self._attributes = attributes + + @classmethod + def from_dict(cls, dikt) -> 'RemoteEvent': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The remote_event of this RemoteEvent. # noqa: E501 + :rtype: RemoteEvent + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this RemoteEvent. + + + :return: The id of this RemoteEvent. + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this RemoteEvent. + + + :param id: The id of this RemoteEvent. + :type id: str + """ + + self._id = id + + @property + def attributes(self): + """Gets the attributes of this RemoteEvent. + + + :return: The attributes of this RemoteEvent. + :rtype: List[Attribute] + """ + return self._attributes + + @attributes.setter + def attributes(self, attributes): + """Sets the attributes of this RemoteEvent. + + + :param attributes: The attributes of this RemoteEvent. + :type attributes: List[Attribute] + """ + + self._attributes = attributes diff --git a/txtapi/txtapi/models/voice_event.py b/txtapi/txtapi/models/voice_event.py new file mode 100644 index 0000000000000000000000000000000000000000..752667045049bffd1143cab42b056ea352eb6391 --- /dev/null +++ b/txtapi/txtapi/models/voice_event.py @@ -0,0 +1,64 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from txtapi.models.base_model_ import Model +from txtapi import util + + +class VoiceEvent(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, text=None): # noqa: E501 + """VoiceEvent - a model defined in OpenAPI + + :param text: The text of this VoiceEvent. # noqa: E501 + :type text: str + """ + self.openapi_types = { + 'text': str + } + + self.attribute_map = { + 'text': 'text' + } + + self._text = text + + @classmethod + def from_dict(cls, dikt) -> 'VoiceEvent': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The voice_event of this VoiceEvent. # noqa: E501 + :rtype: VoiceEvent + """ + return util.deserialize_model(dikt, cls) + + @property + def text(self): + """Gets the text of this VoiceEvent. + + + :return: The text of this VoiceEvent. + :rtype: str + """ + return self._text + + @text.setter + def text(self, text): + """Sets the text of this VoiceEvent. + + + :param text: The text of this VoiceEvent. + :type text: str + """ + + self._text = text diff --git a/txtapi/txtapi/openapi/openapi.yaml b/txtapi/txtapi/openapi/openapi.yaml index 0a69f34ab39c7fb774fe3f59730e1b148f04bded..cf028fe0b513a31259b96b5403999f593de62603 100644 --- a/txtapi/txtapi/openapi/openapi.yaml +++ b/txtapi/txtapi/openapi/openapi.yaml @@ -3,9 +3,16 @@ info: title: ft-controller-api version: 1.0.0 servers: -- url: http://localhost/api/v1 -- url: https://localhost/api/v1 -- url: http://192.168.7.2/api/v1 +- description: HTTP + url: http://localhost/api/v1 +- description: Local + url: http://txt40.local/api/v1 +- description: USB + url: http://192.168.7.2/api/v1 +- description: WLAN-AP + url: http://192.168.8.2/api/v1 +- description: Bluetooth + url: http://192.168.9.2/api/v1 tags: - description: Everything for running applications name: application @@ -2554,9 +2561,58 @@ paths: tags: - util x-openapi-router-controller: txtapi.controllers.util_controller + /remote: + post: + description: Adds a event to the running program on the controller. + operationId: add_remote_event + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/remote_event' + required: true + responses: + "200": + description: OK + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Bad Request + "404": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Not Found + "412": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Pre Condition failed + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Internal Server Error + default: + content: + '*/*': + schema: + $ref: '#/components/schemas/inline_response_default' + description: Unexpected Error! + security: + - ApiKeyAuth: [] + summary: Add a event to the running program + tags: + - remote + x-openapi-router-controller: txtapi.controllers.remote_controller /remote/send-command/{command}: post: - description: send voicecommand + description: DEPRICATED - Adds a text to the running program on the controller. operationId: send_command parameters: - description: The voicecommand to be executed @@ -2602,6 +2658,105 @@ paths: description: Unexpected Error! security: - ApiKeyAuth: [] + summary: DEPRICATED - Add a text to the running program + tags: + - remote + x-openapi-router-controller: txtapi.controllers.remote_controller + /remote/stream: + get: + description: Message stream for events from running programs on the controller. + operationId: remote_event_stream + parameters: + - explode: true + in: query + name: X-API-KEY + required: false + schema: + type: string + style: form + responses: + "200": + content: + text/event-stream: + schema: + type: string + description: OK + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Bad Request + "404": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Not Found + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Internal Server Error + default: + content: + '*/*': + schema: + $ref: '#/components/schemas/inline_response_default' + description: Unexpected Error! + security: + - ApiKeyQueryAuth: [] + summary: Event stream for remote control + tags: + - remote + x-openapi-router-controller: txtapi.controllers.remote_controller + /remote/voice: + post: + description: Adds a text event to the running program on the controller. + operationId: add_voice_event + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/voice_event' + required: true + responses: + "200": + description: OK + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Bad Request + "404": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Not Found + "412": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Pre Condition failed + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/error_message' + description: Internal Server Error + default: + content: + '*/*': + schema: + $ref: '#/components/schemas/inline_response_default' + description: Unexpected Error! + security: + - ApiKeyAuth: [] + summary: Add a text event to the running program tags: - remote x-openapi-router-controller: txtapi.controllers.remote_controller @@ -2899,7 +3054,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/file' + $ref: '#/components/schemas/controller_file' type: array description: OK "400": @@ -3084,7 +3239,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/file' + $ref: '#/components/schemas/controller_file' description: OK "400": content: @@ -3178,17 +3333,20 @@ components: type: string type: object x-body-name: workspace - file: + controller_file: example: path: path + file_content: file_content name: name properties: name: type: string path: type: string + file_content: + type: string type: object - x-body-name: file + x-body-name: controller_file inline_response_default: example: message: message @@ -3596,6 +3754,30 @@ components: type: string type: object x-body-name: controller + voice_event: + properties: + text: + type: string + type: object + x-body-name: voice_event + remote_event: + properties: + id: + type: string + attributes: + items: + $ref: '#/components/schemas/attribute' + type: array + type: object + x-body-name: remote_event + attribute: + properties: + name: + type: string + value: + type: string + type: object + x-body-name: attribute motor_allOf: properties: direction: