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

  • Ergänzend zu ``bottle`` sei noch zusagen, dass hier einiges einfacher gemacht werden kann/sollte. Allein der Teil mit den statischen Dateien. Auch kann dank der Templat-Engine - vor allem wenn das Projekt wächst - einiges vereinfacht werden. Template Dateien enden in bottel mit .tpl und werden im Ordner ``views`` abgelegt. In der ``bottle.py`` ist das so hinterlegt und macht das ganze dann einfacher.
    http://bottlepy.org/docs/dev/stpl.html#simpletemplate-engine


    Auch in der App selber ist das try - except Konstrukt sinnfrei, weil bottle dies bei den Server-APIs bereits mitliefert.
    Eine korrekte Ordnerstruktur könnte wie folgt aussehen:


    - my_porject
    - htdocs # kann frei benannt werden
    - css
    - js
    - img
    - views # muss so benannt werden
    - index.tpl
    - layout.tpl
    - my_app.py



    app.py


    layout.tpl


    index.tpl

    Code
    % rebase('layout', title='Startseite')
    {{values}}


    Edit:
    und die obigen Beispiele halten sich allesamt nicht an die Namenskonvention. z.B. werden oben Funktionen wie Klassen definiert/geschrieben ;)

  • Hier bitte Fragen, Anregungen, Ergänzungen oder allgemeine Diskussionen bezüglich des Threads [Python] Anleitung: Webserver, Websocket und ein bisschen AJAX behandeln damit es dort drüben übersichtlich bleibt!



    Vielen Dank für euer Verständnis!


  • Was für Quellen soll ich bei selbst geschrieben Code denn angeben? Jede Homepage der verwendeten Module oder meine Projekte verlinken, oder was erwartest du? :-/


    Ja genau. Damit der Leser weiss, um was es geht und sich selber ggf. in die Doku einlesen kann.


    zB:
    http://codepad.org/ieQc34zG

    Code
    # http://bottlepy.org/docs/dev/tutorial.html


    Ist im Thread "Anleitung: Webserver, Websocket und ein bisschen AJAX" nicht vorhanden, jedoch im ausgelagerten Code schon.
    Übersichtlicher wäre es m.E., wenn am Ende eines Beitrages oder Abschnitts die jeweiligen Quellen angeben würden:
    http://bottlepy.org/docs/dev/async.html#finally-websockets
    https://bitbucket.org/noppo/gevent-websocket
    etc...


    Hoffe, es ist nun verständlicher, was ich meinte ;)

  • Wenn ich mit allem fertig bin wird alles entrümpelt und auch eine Sektion "Links" vorhanden sein :fies:


    Höhere Priorität hat erst mal der Aufbau, sinnvolle Gestaltung, Vervollständigung bzw Erstellung des Codes (den ich für die Anleitung nämlich aus meinen Projekten raus lutschen muss) und vor allem der Text zwischen den Spoilern, welcher Anfängerfreundlich sein soll...

  • Hallo meigrafd


    Erst mal vielen Dank für deine Initiative in diesem Forum, das ist wirklich nicht mehr mit Worten zu beschreiben, habe schon viel durch dich und auch einige andere hier wie dbv gelernt, ist trotzdem
    nicht einfach für mich da reinzukommen.
    Dieser Beitrag hat mich sehr interessiert, ich bin leider noch nicht so richtig dahinter gestiegen, wie das Zusammenspiele vom onClick in der Html zur js. und die Datenübergabe über .py erfolgt.
    Das ist auch zweitrangig irgendwann wird das schon.
    Beim Testen des letzten Abschnittes, lief leider nur
    die Test Ausgabe, danach habe ich die Klammerungen kontrolliert, in der control.js ist ein Komma und eine )
    zufiel.
    function Send(command) {
    $.ajax({
    type: "GET",
    url: "/cmd/" + command,
    dataType: "JSON",
    success: function(data) {
    mylog("Command Response: " + data);
    if (document.getElementById(command) !== null) {
    document.getElementById(command).innerHTML = data;
    }
    }),
    });
    }



    Nach der Änderung lief alles ausgezeichnet.


    function Send(command) {
    $.ajax({
    type: "GET",
    url: "/cmd/" + command,
    dataType: "JSON",
    success: function(data) {
    mylog("Command Response: " + data);
    if (document.getElementById(command) !== null) {
    document.getElementById(command).innerHTML = data;
    }
    }
    });
    }


    Liebe Grüße alfavita

  • Hallo meigrafd


    Einen Tip, zu deinem letzten Beitrag bräuchte ich.
    Ein Webserver in Python, mit unterschiedlichen "route"s für reines Polling, ohne WebSocket.
    Diesen Code


    Code
    def getGerdUart():
        with open("/temp/text.txt", "r") as u:
            content = u.read()
            return json.dumps(content)


    habe ich in die web_bottle.py sowie entsprechen id in der Html Seite zusätzlich eingefügt, um eine Text Datei auszulesen.
    Diese soll im Original auf der Webseite angezeigt werden. Wird durch den Button die function Send(command) aufgerufen ist die Anzeige korrekt.
    Sobald die Telemetry läuft, wird der Text als String angezeigt:
    "Sensor 1 = 24.40C\r\nSensor 2 = -9.39C\r\nSensor 3 = -9.44C\r\nSensor 4 = -9.34C\r\nSensor 5 = -9.43C\r\nEnde\n\r\nF0\n\r\n"
    In der function Send(command) steht hier data
    document.getElementById(command).innerText = data;)



    In der function get_telemetry an dieser Stelle val, ein ändern auf data hat nicht geholfen.
    Hättes du oder jemand anders einen Lösung ?
    Vielen Dank im voraus. :)


    Liebe Grüße alfavita

  • Guck dir mal die beiden Funktionen genauer an:


    [code=php]
    function Send(command) {
    $.ajax({
    type: "GET",
    url: "/cmd/" + command,
    dataType: "JSON",
    success: function(data) {
    mylog("Command Response: " + data);
    if (document.getElementById(command) !== null) {
    document.getElementById(command).innerHTML = data;
    }
    }
    });
    }[/php]


    Hier wird bei "success" der function etwas übergeben, dem die Variable "data" zugeordnet wird. Folglich ist der Aufruf innerhalb dieser function dann auch "data".


    [code=php]
    function get_telemetry() {
    $.getJSON("/data/")
    .fail(function() {
    console.log("Error processing get_telemetry");
    clearTimeout(telemetryTimer);
    })
    .done(function(data) {
    $.each(data, function(id,val) {
    if (document.getElementById(id) !== null) {
    mylog("JSON Data: " + id + ":" + val);


    if (id == "LoadAVGnum") {
    document.getElementById(id).innerHTML = val + "%";
    } else if (id == "LoadAVGperc") {
    document.getElementById(id).value = val;
    } else if (id == "RAMnum") {
    document.getElementById(id).innerHTML = val + "MB";
    } else if (id == "RAMperc") {
    document.getElementById(id).value = val;


    } else {
    document.getElementById(id).innerHTML = val;
    }
    }
    })
    telemetryTimer = setTimeout(get_telemetry, 2000);
    });
    } [/php]


    Hier wird durch $.getJSON bei Erfolg ".done" ausgeführt und ebenfalls einer function etwas übergeben, was ebenfalls als "data" zugeordnet wurde. Allerdings gibt es hier eine Besonderheit: $.each ... Das Teilt den Inhalt von "data" in zwei weitere Variablen auf: id und val. Da die empfangenen Daten im JSON Format vorliegen, entspricht der Schlüssel => id und der Wert => val ... val steht für value.


    Wenn du die URI / URL / route direkt ansurfst siehst du wie die JSON Daten aussehen. Da ich deine route nicht kenne, sag ich jetzt einfach mal als Beispiel: http://192.168.0.10/cmd/uart ...und... http://192.168.0.10/data/
    Bitte in CODE posten.
    Beachte dass das 2 unterschiedliche route's sind, also bitte die Ausgaben passend zur jeweiligen URL posten, auch wenn in beiden das gleiche stehen würde...

  • Hallo maigrafd
    das ist der Code cmd/PiUART

    Code
    "Kessel I = aus\r\nKessel II= aus\r\nVentil 1 = auf\r\nVentil 2 = auf\r\nSensor 0 = 17.95C\r\nSensor 1 = 24.40C\r\nSensor 2 = -9.32C\r\nSensor 3 = -9.43C\r\nSensor 4 = -9.21C\r\nSensor 5 = -9.49C\r\nT6012345S100179S100244S000094S000094S000093S000095\n\r\nF0\n\r\n"



    bei data

    Code
    {"PiTEMP": 49.388, "PiUART": "\"Kessel I = aus\\r\\nKessel II= aus\\r\\nVentil 1 = auf\\r\\nVentil 2 = auf\\r\\nSensor 0 = 17.95C\\r\\nSensor 1 = 24.40C\\r\\nSensor 2 = -9.37C\\r\\nSensor 3 = -9.46C\\r\\nSensor 4 = -9.33C\\r\\nSensor 5 = -9.44C\\r\\nT6012345S100179S100244S000094S000094S000094S000094\\n\\r\\nF0\\n\\r\\n\"", "PiRAM": 840672}


    Danke für die schnelle Antwort

    Edited once, last by alfavita ().

  • Die Ausgaben sind also unterschiedlich - mal von PiTEMP und PiRAM abgesehen. In der Ausgabe über "/data/" sind mehr \ zu sehen als in der Ausgabe über "/cmd/PiUART"


    Wie sehen deine jeweiligen route's im Python Script aus? Also auch die Funktionen dazu posten ;)


    Und wie erstellst du die /temp/text.txt Datei?
    (wieso eigentlich /temp/ und nicht /tmp/ ?)

  • hallo meigrafd


    das ist der Code web_bottle.py die Einrückungen werden durch das kopieren nicht richt eingefügt
    aber der Scrip läuft.




    Hier das Python Script wo die Daten vom Gertboard ausgelesen werden


    In der Text Datei steht das


    Kessel I = aus
    Kessel II= aus
    Ventil 1 = auf
    Ventil 2 = auf
    Sensor 0 = 17.95C
    Sensor 1 = 24.40C
    Sensor 2 = -9.40C
    Sensor 3 = -9.42C
    Sensor 4 = -9.33C
    Sensor 5 = -9.45C
    T6012345S100179S100244S000094S000094S000094S000094


    F0


    warum nicht tmp


    Ich hatte einen Beitrag gelesen in dem beschrieben wurde wie man ein Verzeichnis/Datei in den RAM auslagert um die SD Karten zu schonen, wegen der schreib -und Lesezugriffe.
    Dazu habe ich mir temp angelegt, weil ich nicht weiß ob es im Pi zu Störungen kommen könnte wenn ich das bei tmp gemacht hätte.

    Edited once, last by alfavita ().

  • Wieso hast du das auslesen der UART denn in ein separates Scripts aufgeteilt?
    Du hast da aber auch noch einen groben Schnitzer in deinem extra Script: Du öffnest warum auch immer die gleiche Datei noch mal zum lesen ("r") und gibst das dann auf der Konsole aus - anstatt direkt das geschriebene auszugeben - aber schließt den Datei-Handler dann nicht mehr. Irgendwann platzt der Kessel und Linux verweigert weiteres öffnen irgendwelcher Handler, was sogar zum crash des Systems führen kann. Du hast 2x "ndatei.close()" das letzte sollte aber "dateineu.close()" lauten.


    Und was hat es mit /temp/ auf sich, wieso nicht /tmp/ ?


    Ich glaub ich seh das Problem - kann ich gerade aber nicht selber ausprobieren da mein Pi zu tun hat.... Und zwar wird in der "/data/" route die Rückgabe tatsächlich in json verpackt, wohingegen aber die "/cmd/" route die Rückgabe nur als String zurück gibt aber nicht in jason verpackt...


    Ohne das jetzt irgendwie um zu schreiben könntest du einfach folgendes machen:


    Du fügst in der control.js Datei in "function get_telemetry() {" einfach ein "Send("PiUART");" ein:
    [code=php]
    function get_telemetry() {
    Send("PiUART");
    $.getJSON("/data/")
    .fail(function() {
    console.log("Error processing get_telemetry");
    clearTimeout(telemetryTimer);
    })
    .done(function(data) {
    $.each(data, function(id,val) {
    if (document.getElementById(id) !== null) {
    mylog("JSON Data: " + id + ":" + val);


    if (id == "LoadAVGnum") {
    document.getElementById(id).innerHTML = val + "%";
    } else if (id == "LoadAVGperc") {
    document.getElementById(id).value = val;
    } else if (id == "RAMnum") {
    document.getElementById(id).innerHTML = val + "MB";
    } else if (id == "RAMperc") {
    document.getElementById(id).value = val;


    } else {
    document.getElementById(id).innerHTML = val;
    }
    }
    })
    telemetryTimer = setTimeout(get_telemetry, 2000);
    });
    }
    [/php]


    Musst dann aber die Zeile "data["PiUART"] = getGerdUart()" im Python Script, in der TelemetryHandler() Funktion, rauswerfen. Das ist keine besonders tolle Lösung aber sollte erst mal funktionieren :blush:


    Du solltest dir aber Gedanken darüber machen das PiUART Konstrukt allgemein zu ändern ;)

  • Hallo meigrafd
    Das mit dem 2. Script und der Ausgabe auf die Konsole ist nur für Testzwecke gedacht, ich lerne immer noch, danke für dein Tipps und die Hilfe.
    Wenn das alles läuft kommt er mit in die web_bottle.py
    Ich mache mir sehr viele Gedanken zu den ganzen Sachen ist eben nicht alles so einfach für mich,
    aber macht sehr viel Spaß sich auch über kleine Erfolge zu freuen.



    Liebe Grüße alfavita

  • Kann es sein dass tornado überarbeitet worden ist, und der Code so nicht mehr funktioniert?


    Traceback (most recent call last):
    File "py_tornado.py", line 6, in <module>
    class Application(tornado.web.Application):
    AttributeError: 'module' object has no attribute 'web'

  • Laut der Dokumentation nicht: http://www.tornadoweb.org/en/stable/ beziehungsweise http://www.tornadoweb.org/en/stable/guide/structure.html beziehungsweise http://www.tornadoweb.org/en/stable/web.html


    Allerdings muss ich gestehen das ich primär die bottle Scripts verifiziert habe da ich bottle bevorzuge


    Ändere bzw erweitere einfach die Zeile
    [code=php]
    import tornado
    [/php]


    So ab dass dort dann das steht:
    [code=php]
    import tornado.web
    import tornado.httpserver
    import tornado.ioloop
    [/php]


    dann sollte es funktionieren.


    Werde ich morgen in der Anleitung nachbessern.

  • Hallo meingrafd


    Vielen Dank für die Verlinkung zu deinem Tutorial


    Ich habe es nun nachgebaut (ohne json, mit tornado) Allerdings habe ich nun das Problem, dass die Verwindung aus dem Browser zum Script zwar hergestellt wird, allerdings wird der Befehl nicht gesandt bzw. empfangen. Bei einem klick auf den Button CPU Temp passiert... gar nichts.


    Folgendes passiert beim Laden der Seite im Browser mit "meineIP:8080"


    im Terminal Raspi wird folgendes angezeigt

    Code
    WARNING:tornado.access:404 GET /static/control.js (meine IP) 13.44ms
    WARNING:tornado.access:404 GET /static/websocket.js (meine IP) 4.97ms



    Die Websiete zeigt:
    WebSocket Status: closed


    Test: 757



    Firebug zeigt:

    Code
    "NetworkError: 404 Not Found - http://192.168.2.27:8080/static/control.js"             control.js
    "NetworkError: 404 Not Found - http://192.168.2.27:8080/static/websocket.js"           websocket.js
    ReferenceError: init is not defined                                                    192.168.2.27:8080 (Zeile 1, Spalte 1)



    Wirst du daraus schlau? Google konnte mir bisher nicht helfen


    Gruss GL

  • Dir fehlen die JS-Dateien, die er aus /static holen will. Da du keinen Code zeigst, kann man dazu nix sagen warum das so ist.

  • Wie in der Anleitung beschrieben benötigst du folgende Verzeichnisstruktur:


    /home/pi/web_tornado.py
    /home/pi/templates/index.html
    /home/pi/static/control.js
    /home/pi/static/websocket.js


    Wobei es keine Rolle spielt ob die Dateien in /home/pi/ oder /root/ oder sonst wo liegen. Wichtig ist allerdings in Deinem speziellen Fall, das im selben Verzeichnis wo das Python Script liegt auch das Verzeichnis "static/" sowie die darin befindlichen Dateien vorhanden sind.


    Deine Fehlermeldung enthält einen einheitlichen HTML Error/Status Code: 404 Not Found ... Die angeforderte Ressource wurde nicht gefunden. => https://de.wikipedia.org/wiki/…x_.E2.80.93_Client-Fehler
    Das heißt er kann die angeforderte Datei nicht finden - quasi: File not found.
    Folgefehler davon ist dann auch dass der Button keine Reaktion zeigt, weil das von JavaScript abhängig ist und das im control.js hinterlegt ist was da passieren soll...