[Python] Diskussion/Fragen: Webserver, Websocket und ein bisschen AJAX

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo Zusammen

    vielen Dank für die Antworten.

    das habe ich mir gedacht. allerdibhs existieren die Dateien und befinden sich auch im static ordner

    wenn ich den link im girebug asnklicke wird die Datei acu geöffnet.

    Der code ist eins zu eins jener von meingrafd.

    Gruss GL


    Gesendet von meinem SM-G935F mit Tapatalk

  • [Python] Diskussion/Fragen: Webserver, Websocket und ein bisschen AJAX? Schau mal ob du hier fündig wirst!

  • Naja, irgendwo musst Du einen Fehler gemacht haben - denn der Code funktioniert sowohl bei mir als auch bei anderen, nur du scheinst Probleme damit zu haben....
    Ich kann dir auch gerne ein Video zeigen - wenn man wirklich so vorgeht wie in der Anleitung beschrieben dann funktioniert's auch => http://raspberrypi.roxxs.org/web_tornado.avi ... Bei Position 0:03 siehst du die Verzeichnisstruktur. Anschließend prüfe ich den Inhalt der Unterverzeichnisse.. Alle erforderlichen Dateien sind vorhanden und auch befüllt.. 1:1 wie in der Anleitung beschrieben. Dann starte ich das Script und surfe meinen Pi auf Port 8080 an - so wie per Default im web_tornado.py eingestellt... Und siehe da, es funktioniert.

    Die Meldung bei dir lautet nur das er versucht die Datei zu öffnen aber eben nicht finden kann also fehlschlägt. Wenn er sie tatsächlich erfolgreich geöffnet hätte aber dann ein Fehler auftritt säh die Fehlermeldung anders aus.

    Ohne weitere Infos können wir dir da nicht helfen. Beispielsweise ein "ls -la" von dem Verzeichnis posten in dem deine web_tornado.py liegt (bitte in CODE)

  • Guten Morgen,
    Ich habe mich strickt an Anleitung gehalten und bekomme vollgende Meldung siehe Bild
    Kann mir jemand weiterhelfen?
    e616ee044f46842bce77d1c9fae532a7.jpg

    Vielleicht macht es auch Sinn bei mir wieder bei 0 anzufangen.
    Dafür würde ich gerne wissen wenn der pi später vollende Aufgaben übernehmen soll welche Sachen ich installieren bzw brauche.

    Code
    1. Relais an aus über Handy oder Webseite ( nur lokal)
    2. wen bis X Uhr nicht 2 std Relais 1 an dann für Rest laufen
    3. Relais 2 bei Temperatur Differenz 2 er Eingänge ein 
    4. Sperre 1 gegen 2

    nicht das ich mir den Pi zu Mülle da ich Anfänger bin [emoji848]

    Einmal editiert, zuletzt von B4unty (3. Juni 2017 um 09:02)

  • Da du keinen Shebang gesetzt hast musst du die Datei dem Interpreter übergeben.
    Da du "./web_tornado.py" eingibst zum ausführen versucht er es mit bash zu verarbeiten aber das wird freilich nichts - unterschiedliche Syntax.

    Du hast 2 Möglichkeiten:

    1)

    Code
    python web_tornado.py

    2)
    web_tornado.py bearbeiten und ganz oben in die erste Zeile den Shebang einfügen:

    Python
    #!/usr/bin/python


    erst anschließend direkt ausführen:

    Code
    ./web_tornado.py


    PS: In der Anleitung steht übrigens nirgends das man es mit " ./ " ausführen soll, da steht nur "ausführen" oder "Führt nun ein Python Script eurer Wahl (bottle oder tornado) aus".
    Aber vor allem: "Im derzeitigen Zustand der Python Scripts wie ich sie gleich zeige spielt es eigentlich noch keine Rolle ob man sie mit python2 oder python3 ausführt.". Deshalb gibts auch kein Shebang in den Vorlagen.

  • Hallo!
    Vielen Dank für diese umfassende Anleitung.

    Mit meinen PC kann ich ohne Probleme eine Verbindung herstellen, nur leider klappt das nicht mit meinem IPhone.

    Hat da irgendjemand eine Idee, woran das liegen könnte?

    Ich bekomme in der Konsole immer folgende Meldung:

    New WebSocket Client connected

    WebSocekt closed 1006 Going away

    Gruß

  • Hallo Meigrafd,

    vielen Dank für deine Vorstellung zu JSON als Websocket Alternative!

    Ich probiere gerade das richtig zu verstehen was du das gemacht hast.

    Mein RASPICAR möchte entsprechend über Web steuern und JSON über für die Datenübertragung benutzen.

    Einen Servo kann ich per Knopfdruck von der Seite jetzt in Position fahren lassen.

    Diesen Positionswert würde ich gerne via Regler auf der HTML Seite bestimmen. (Die Antriebsgeschwindigkeit soll später ebenfalls auf diese Weise eingestellt werden).

    Die Python Datei besteht im wesentlichen aus deiner Vorgabe mit Bottle, ich poste hier nur die Änderungen damit es übersichtlicher ist.

    from __future__ import print_function

    import os.path

    import bottle

    import json

    from datetime import timedelta

    from random import randrange

    import RPi.GPIO as GPIO

    import time

    import os

    import svtor

    import threading

    import config

    from RPIO import PWM

    PIN_OUT_1 = 8

    PIN_OUT_2 = 10

    PIN_RECHTS = 12

    PIN_LINKS = 11

    PIN_SERVO_UNTEN = 13

    PIN_SERVO_OBEN = 15

    GPIO.setmode(GPIO.BOARD)

    GPIO.setup(PIN_OUT_1, GPIO.OUT)

    GPIO.setup(PIN_OUT_2, GPIO.OUT)

    GPIO.setup(PIN_RECHTS, GPIO.OUT)

    GPIO.setup(PIN_LINKS, GPIO.OUT)

    GPIO.setup(PIN_SERVO_OBEN, GPIO.OUT)

    GPIO.setup(PIN_SERVO_UNTEN, GPIO.OUT)

    DEBUG = 1

    servo_unten = PWM.Servo()

    servo_oben = PWM.Servo()

    def servo_unten_func():

    servo_unten.set_servo(PIN_SERVO_UNTEN, rangepwm)

    time.sleep(.5)

    servo_unten.stop_servo(PIN_SERVO_UNTEN)

    @bottle.route('/data/')

    def TelemetryHandler():

    printD("Telemetry Request.")

    bottle.response.content_type = 'application/json'

    data = {}

    data["PiTEMP"] = getPiTemperature()

    data["PiRAM"] = getPiRAM()

    data["servo_untenlinks"] = servo_unten_func()

    rangepwm = data["MotorSpeed"]

    return json.dumps(data)

    @bottle.route('/cmd/<command>')

    def CommandHandler(command):

    response = ''

    printD("Command Request: {}".format(command))

    if command == 'PiTEMP':

    response = getPiTemperature()

    elif command == 'PiRAM':

    response = getPiRAM()

    elif command == 'servo_untenlinks':

    response = servo_unten_func()

    elif command == 'exit':

    response = beenden()

    printD("response: %s"% response)

    return str(response)

    Hier die HTML Datei, die control.js habe ich nicht angefasst.

    <!DOCTYPE html>

    <html>

    <head>

    <script>

    var DEBUG = {{ debug }};

    </script>

    <script src="/static/jquery-3.3.1.min.js" type="text/javascript"></script>

    <script src="/static/control.js" type="text/javascript"></script>

    </head>

    <body>

    Test: {{ test }}

    <br/>

    <input type="button" value="RAM" onClick="Send('PiRAM')" />

    <span id="PiRAM"></span>

    <br/> <input type="button" value="CPU Temperatur" onClick="Send('PiTEMP')" />

    <span id="PiTEMP"></span> &deg;C

    <br/>

    <br/> <input type="button" value="Servo unten links" onClick="Send('servo_untenlinks')" />

    <span id=MotorSpeed></span>

    <br/>

    <br/> <input type="button" value="Exit" onClick="Send('exit')" />

    <span id="Exit"></span>

    <br/>

    <p><div id="Log" style="text-align:left; width:100%;">&nbsp;</div></p>

    <form oninput="numerisch.value=auswertung.value">

    <input type="range" name="auswertung" min="100" max="2000" value="500">

    PWM: <output name="numerisch">500</output>

    </form>

    </body>

    </html>

    Beim Versuch rangepwm an die Funktion zu übergeben, bekomme ich die Meldung das diese global nicht deklariert ist. So kann es also nicht funktionieren.

    Wie kann ich richtig auf die JSON Daten zugreifen, innerhalb einer anderen Funktion?

    Die Daten scheinen ja nur innerhalb dieser bottle funktion nutzbar zu sein, zwar kann ich andere Funktionen von dort aus aufrufen, aber die Daten nicht bidirektional verwenden.

    Wie würdest du das machen?

    Danke und Gruß Tenor

  • tenor da sich meigrafd hier schon länger nicht mehr hat blicken lassen, wirst du bei einer direkten Frage an Ihn wohl keine Antwort bekommen. Bitte stelle deine Frage erneut in einen neuen Thread und achte dabei auf folgendes:

    • Such das passende Subforum raus
    • Formatiere deinen Code auch als solchen:
      Python
      Python-Code
    • Poste vollständige Fehlermeldungen
    • Vermeide schwammige Ausdrücke wie "über Web senden" (:no_sad:)
    • Stelle die Frage allgemein, nicht an meigrafd gerichtet.


    :danke_ATDE: an dich und noisefloor!

  • Hallo Leute,

    bitte seid gnädig mit mir. Dies ist mein erster Post hier.

    Ich habe das Tutorial durchgearbeitet und beide (bzw. alle 3) Varianten ausprobiert. Variante 3 schien mir am sinnvollsten (schlank und wenig Datenverkehr). Ich habe das Beispiel erst einmal nahezu unverändert gelassen und augenscheinlich läuft auch das meiste. Ich kann per Befehlseingabe (z.B. IP/cmd/PiTemp) eine Ausgabe befehlen und auch über IP/data die vom Script vorgesehen Werte sehen (sowohl auf der html Seite wie auch in der Kommandozeile die entsprechende Informationen).

    Nun stehe ich aber vor dem Problem, dass weder meine Button, noch die Anzeige des Variableninhaltes auf der Seite funktionieren. Die Variable (bzw. deren Platzhalter) auf der Seite bleibt leer und wenn ich einen der Button drücke, passiert nichts (weder auf der Seite, noch sehe ich etwas in der Kommandozeile). Habe jetzt stundenlang auch andere Seiten und Tutorials gesichtet, aber da sieht die Syntax "Send" völlig anders aus, als im Turorial. Irgendwie wirkt das auf mich, als sei die Kommunikation bzw. der Aufruf zum/an den CommandHandler nicht da.


    jquery... ist natürlich ebenso im => static Verzeichnis, wie die control.js.

    Die index.html liegt in => templates


    Wenn ich das irgendwie hinbekommen möchte ich nachher in meinem Projekt eigentlich am Ende "nur" 2 Anzeigen (Pythonvariablen) auf der Website sehen, die zyklisch bzw. bei Aufruf aus Python aktualisiert werden und eventuell noch 2-3 Befehle an eine Javascriptdatei schicken. Also wirklich nichts wildes.


    Kann mir jemand einen Tipp geben? :/


    Hier die web_bottle.py


    Hier die Html


    Habe auch in der control.js zwischendurch die Klammern vor/hinter der telemetryTimer - Funktion entfernt, hatte damit aber auch keinerlei Änderung erreichen können.

  • Anmerkungen zum Python-Quelltext:

    `timedelta` wird importiert aber nirgends verwendet. Aus `bottle` werden explizit Namen importiert die alle nicht verwendet werden. `print_function` aus `__future__` importieren macht keinen Sinn mehr in Python 3.

    In neuem Code verwendet man eher `pathlib` als die alten Funktionen aus `os.path`.

    Namen KOMPLETT_IN_GROSSBUCHSTABEN sind Konstanten, die sollte man nicht verändern. Die Vorlagen gehören in ein Verzeichnis das `views/` heisst, dann muss man nicht an `bottle.TEMPLATE_PATH` herumpfuschen.

    Python hat einen Datentyp für Wahrheitswerte (`bool`) und die Werte `True` und `False`. Dafür sollte man keine Zahlen missbrauchen.

    Wenn es schon eine Konstante `DEBUG` gibt, sollte die auch für `bottle.debug()` verwendet werden, statt da dann noch mal hart einen Wahrheitswert zu kodieren.

    Debug/Logging-Ausgaben sollte man nicht selbst programmieren. Es gibt das `logging`-Modul in der Standardbibliothek. Und Bottle gibt Anfragen bereits selbst aus, das muss man nicht selbst machen.

    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

    `tmp` in `getPiRAM()` ist überflüssig, da sollte man gleich den Namen verwenden an den der Wert am Ende gebunden sein soll.

    `usedPerc` wird nicht verwendet und `used` nur um `usedPerc` zu berechnen und damit wird dann auch `total` nicht verwendet.

    `mem` und `sline` sind schlechte Namen, weil sie dem Benutzer nicht verraten was die Werte dahinter bedeuten. `i` für etwas das keine ganze Zahl, sondern eine Zeile/Zeichenkette ist, ist ebenfalls schlecht.

    Die Elemente nach einem `split()` auf einer Zeichenkette sind bereits Zeichenketten, da macht `str()` keinen Sinn.

    Wenn man nur die erste Zeile einer Datei verarbeiten möchte muss man nicht die komplette Datei einlesen.

    Wenn für eine Route ein Wörterbuch zurückgegeben wird, dann kümmert sich Bottle darum JSON auszuliefern.

    Ein leeres Wörterbuch erstellen um dann in den nächsten Zeilen einzelne Schlüssel/Wert-Paare hinzuzufügen ist umständlich. Man würde da einfach gleich das Wörterbuch mit den Werten hinschreiben.

    Der `content_type` sollte bei `static_file` automatisch ermittelt werden. Falls es nicht klappt, dann kann man dieser Funktion den MIME-Typ als Argument mitgeben.

    Bei `CommandHandler` sollte ein unbekanntes Kommando nicht einfach mit einer leeren Zeichenkette beantwortet werden.

    Ungetestet:

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Hallo Blackjack

    danke, dass du dir die Zeit genommen hast. Wie geschrieben, habe ich den Code aus den Vorlagen und den Großteil davon verstehe ich noch nicht in gänze. Nichtsdestotrotz erscheint mir deine Lösung sehr viel schlanker und das ist immer gut.

    Leider bekomme ich bei deinem Code das exakt gleiche Ergebnis. Nachdem ich die Index.html in ein neues Verzeichnis "views" importiert habe, stellt sich die Website genau wie vorher da. Über "ip/cmd/PiTemp" bekomme ich die Temperatur angezeigt und sehe in der Kommandozeile auch die entsprechenden Informationen. Aber leider reagiert der Browser (und auch die Kommandozeile, sprich das Programm) nicht auf den Klick auf einen der Button und es werden auch keine Werte (außer dem Testzähler) mit Werten beschrieben.

    Also, außer das der Code jetzt deutlich schlanker ist, bin ich kein Stück weiter. Hast du noch eine Idee?

    Einmal editiert, zuletzt von homiys (19. März 2024 um 17:04)

  • Danke hyle, dass auch du dir die Zeit genommen hast. Das war natürlich auch einer meiner ersten Gedanken. Zum testen hatte ich zwischendurch ein (Aufruf innerhalb der Index.html, Ausführung ohne Datenausausch mit bottle) Javascript hinzugefügt, welches ohne Probleme auf der Seite ausgeführt wurde. Zudem habe ich mich mit einem weiteren Rechner auf die Website aufgeschaltet, auch da genau das gleiche. Der Raspberry läuft über einen kleinen extra Router, der innerhalb seines Netzes auf absoluten Durchzug (keine FW) steht. Daran sollte es also nicht liegen.

    Ich vermute eher, es liegt irgendwo an der Control.js, könnte das vielleicht sein?

    Vielleicht sollte ich das Thema auch noch einmal für sich eröffnen, möchte hier nur niemanden seine Zeit stehlen.

    Einmal editiert, zuletzt von homiys (19. März 2024 um 17:14) aus folgendem Grund: Etwas uneindeutig beschrieben.

  • Zitat

    <script src="/static/jquery.min.js" type="text/javascript"></script>
    <script src="/static/control.js" type="text/javascript"></script>

    Das sind ja absolute Pfade vom Document-Root des Webservers aus gesehen. Passt das auch zu Deinen Gegebenheiten?

    Vielleicht passen da einfach nur die Pfade nicht.

  • Hallo hyle ,

    tatsächlich war das allein nicht die richtige Lösung, aber der richtige Weg dahin. Deine Nachfrage hat mich dazu gebracht alle Pfade noch einmal zu testen bzw. zu prüfen. Der Aufruf der jquery war tatsächlich fehlerhaft (in der Vorlage ist es "nur" ...jquery.min.js), runtergeladen soll aber jquery-1.12-3-min.js...

    Diesen Aufruf habe ich nun korrigiert, und siehe da, es funktioniert. :)

    Vielen Dank :thumbup:


    Sag mal, du weißt nicht zufällig, ob ich auf diese Weise auch ein Javascript auf der Seite ausführen kann (also wenn ich eine Variable mit dem entsprechenden Command schicke)?

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!