Ankündigung

Einklappen
Keine Ankündigung bisher.

Read: Wie funktioniert das mit dem Index?

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Read: Wie funktioniert das mit dem Index?

    Kann mir bitte jemand die Sache mit dem Index erklären beim "read" erklären? Ich werde aus der Doku nicht schlau: https://github.com/CometVisu/CometVi...oll#Lesen_read

    Mein bisheriges Verständnis:

    Wenn 10 Adressen gelesen werden sollen, es aber timeout-bedingt nur für die ersten 5 reicht, dann steht in der Antwort index=5.
    Wenn alles gelesen wurde, steht index=10. Und bei der nächsten Anfrage müsste der Index dann wieder verschwinden/bei 0 beginnen?!

    Egal wie ich es drehe und wende: Der Client fragt _jedesmal_ wieder mit dem Index an den ich ihm gegeben hatte.
    Wann sollte man den Index denn nun inkrementieren?

    #2
    Nachdem das mit dem Echtzeit-Prohlem vermutlich geklärt ist (https://knx-user-forum.de/forum/suppo...daten-aber-wie), habe ich eine Mögliche Erklärung für den Index:

    Schritt 1) Client frägt beispielsweise drei Adressen an, Index nicht mitübergeben --> Client weiß noch von gar nix und will alle drei W erte auf einmal.
    Das Backend muss nun für alle 3 Adressen werte liefern und antwortet mit drei Werten und einem Index von sagen wir "1".

    Schritt 2) Der Client ist erstmal zufrieden und frägt wieder alle drei Adressen an. Dieses mal mit Index 1.
    Dieses mal ist der Abfrage blockierend, bis sich mindestens eine der drei Adressen ändert.
    Das Backend teilt die Änderung (und nur die Änderung, also nicht unbedingt alle drei Werte, sondern minimal 1, maximal 3) mit und inkrementiert den Index auf 2.

    Schritt 3) Erneut wird der Client alle drei Adressen anfragen, mit Index=2.
    Das Spiel läuft wieder wie bei Schritt 2, nur dass erneut inkrementiert wird.

    Kommt eine Lese-Anfrage mit nicht vorhandenem Index, wird alles verworfen und komplett neu übermittelt (wie Schritt 1)

    Was mir noch nicht einleuchtet: Der Index kann theoretisch statisch bleiben, oder? Damit könnte man im Backend einen Adress-Listener (der auf dem Bus nach Änderungen lauscht) referenzieren der auf die genannten Adressen aus Schritt 1 lauscht. Wird der Index irgendwann nicht mehr angefragt, kann man den Adress-Listener wieder aufräumen.

    Wäre das so im Sinne des Erfinders?

    Wenn ja: Dann sollte man das Wiki dahingehend erweitern/dokumentieren (was ich auch tun werde wenn hier jemand bestätigen kann dass ich es richtig verstanden habe).

    Kommentar


      #3
      Zitat von tuxedo Beitrag anzeigen
      Was mir noch nicht einleuchtet: Der Index kann theoretisch statisch bleiben, oder? Damit könnte man im Backend einen Adress-Listener (der auf dem Bus nach Änderungen lauscht) referenzieren der auf die genannten Adressen aus Schritt 1 lauscht. Wird der Index irgendwann nicht mehr angefragt, kann man den Adress-Listener wieder aufräumen.

      Wäre das so im Sinne des Erfinders?
      Den Index musst Du die eher wie eine Zeit vorstellen, der Client teilt dem Backend damit mit, dass es alle Änderungen bis zum index-Zeitpunkt kennt. Wenn der Client jetzt eine neue Read-Anfrage mit z.B. i=100 sendet und das Backend in der Zwischenzeit schon Änderungen mitbekommen hat und z.b. bei i=103 ist, dann weiß das Backend genau, welche Änderungen es dem Client senden muss. In diesem Beispiel würde der Client aus sofort eine Antwort vom Backend bekommen mit den besagten Änderungen und nicht erst wenn neue kommen.

      Du musst da immer im Hinterkopf haben, dass der Client in dem Moment wo er eine Antwort auf seinen Read-Request bekommt, diesen erstmal verarbeitet und dann einen neuen Read-Request aufbaut (long-polling Prinzip) und in dieser Zeit hat er keine Verbindung zum Backend und würde Änderungen die in dieser Zeitspanne passieren nicht mitbekommen ohne den index. Ich hoffe ich habe das einigermaßen verständlich erklärt ;-)
      Gruß
      Tobias

      Kommentar


        #4
        Ah, das ergibt Sinn.

        D.h. der Index ist mehr soetwas wie eine Zustands-Revision. Ändert sich der Zustand der angefragten Adressen, ändert sich die Revision (bzw. der Index).

        Das Wort "Index" ist, je nachdem welchen Hintergrund man hat, etwas ungeschickt gewählt. Bei mir ist Index eher eine Position in einer Liste, einem Array oder sowas in der Art.

        Überlege gerade wie ich das am besten im Backend abbilde ...

        Ich muss ja auf Adressänderungen hören, auch wenn der Client gerade einen gerade gemachten Read-Request verarbeitet und deshalb gerade keine Verbindung zum Backend hat.
        Ich meine: Der Client kommt ja nicht und sagt: "Ab jetzt interessiere ich mich für Adressen A, B und C" und eine Weile später meldet er sich erneut und gibt bekannt "A, B und C sind ab sofort uninteressant. "
        So hab ich ja keinen Indikator wann ich aufhören kann auf A, B und C Änderungen zu lauschen (und deren Index hoch zu setzen) und stattdessen auf D, E und F lauschen muss....

        Die naivste Lösung wäre: Auf alles lauschen was am Bus passiert und einen globalen Index für das Gesamtsystem führen (quasi der Zeitstempel der letzten Zustandsänderung überhaupt).
        Aber damit würde, wenn sich der Client für A, B und C interessiert und eine Änderung für D eintrifft, für die sich der Client gar nicht interessiert, auch zu einem (unnötigen) read-response mit nicht wirklich einer für den Client relevanten Änderung führen....

        Eine andere Idee wäre, den Index mit etwas zu präfixen. Einer Art extra Session-ID für diesen Client für diese Adress-Konstellation. Der Read-Request für A, B und C bekommt dann beispielsweise den fiktiven Präfix "ABC-", gefolgt vom Index. Dann könnte das backend genau für diese Adresskonstellation (A, B und C) auf änderungen lauschen, und genau für diesen Index ein read-response geben wenn sich etwas ändert. Währenddessen könnte ein zweiter Client sich für D, E und F interessieren und hätte mit dem Prefix "DEF-" vor dem Index seinen eigenen Index. Man müsste nur die Adress-Listener für die einzelnen Read-Requests ab und zu mal aufräumen (da ein Client ja nicht wirklich bekannt gibt dass er sich für A, B und C nicht explizit bekannt gibt dass er sich nicht mehr für interessiert.). Aber das ließe sich evtl. indirekt über die SessionID erkennen: Eine SessionID wird ja wohl weniger zwei read-requests parallel für unterschiedliche Adressen machen, oder? Wenn eine SessionID ihren read-request ändert, verfällt der bisherige Adress-Listener und ein neur wird gesetzt (das würde auch das prefixen unnötig machen....).
        Hmm, Ideen über Ideen.
        Zuletzt geändert von tuxedo; 07.10.2015, 19:47.

        Kommentar


          #5
          Da du ja wie ich gerade gelesen habe ein eigenes Java backend baust, kann ich ja ein paar Erfahrungswerte aus der Entwicklung das openHab-CometVisu Backend s mit dir teilen.

          Im Grunde gibts da zwei Wege:

          1. Session basiert, wie du schon beschrieben hast. So wirds im openhab 1 Backend gemacht. Das nutzt Atmosphere/Jersey das einem da ne Menge Arbeit abnimmt. Da gibts z. B. den BroadcasterCache, der Session basiert alle Änderungen zwischenspeichern solange der Client gerade keine Verbindung hat. Kannst da gerne im openhab backend spicken.

          2. Statt Long-polling SSE (Server Send Events) nutzen. Ist wie long polling nur das die Verbindung nicht jedesmal unterbrochen wird und somit muss mann sich auch nicht drum kümmern irgendwas zwischen zu speichern während die Verbindung weg ist. Das wird im openHab2-CometVisu Backend verwendet. Auch hier spicken ausdrücklich erlaubt ;-)

          Wenn Du eine Empfehlung brauchst nimm den 2. Weg. Der Atmosphere/Jersey Kram macht in openhab1 immer mal wieder Probleme und ist deswegen auch in der nächsten Version nicht mehr drin.
          Gruß
          Tobias

          Kommentar


            #6
            Zitat von peuter Beitrag anzeigen
            Da du ja wie ich gerade gelesen habe ein eigenes Java backend baust, kann ich ja ein paar Erfahrungswerte aus der Entwicklung das openHab-CometVisu Backend s mit dir teilen.

            Im Grunde gibts da zwei Wege:

            1. Session basiert, wie du schon beschrieben hast. So wirds im openhab 1 Backend gemacht. Das nutzt Atmosphere/Jersey das einem da ne Menge Arbeit abnimmt. Da gibts z. B. den BroadcasterCache, der Session basiert alle Änderungen zwischenspeichern solange der Client gerade keine Verbindung hat. Kannst da gerne im openhab backend spicken.
            Meine Implementierung verwendet keine mächtigen Frameworks. Ist eher ein "KISS"-Ansatz: Möglichst wenig Abhängigkeiten, möglichst keine Code-Monster. Die HTTP-Implementiertung ist mit rund 2000 Zeilen ein absoluter Winzling. Funktioniert aber bestens.

            2. Statt Long-polling SSE (Server Send Events) nutzen. Ist wie long polling nur das die Verbindung nicht jedesmal unterbrochen wird und somit muss mann sich auch nicht drum kümmern irgendwas zwischen zu speichern während die Verbindung weg ist. Das wird im openHab2-CometVisu Backend verwendet. Auch hier spicken ausdrücklich erlaubt ;-)

            Wenn Du eine Empfehlung brauchst nimm den 2. Weg. Der Atmosphere/Jersey Kram macht in openhab1 immer mal wieder Probleme und ist deswegen auch in der nächsten Version nicht mehr drin.
            Öhm... Bei meinen bisherigen Tests hat der Client die Verbindung, solange ich eine Seite nicht gewechselt habe, offen gelassen. Zumindest hat mein Backend diese nicht von sich aus zu gemacht.
            Was ich daran noch nicht verstehe (und ich befürchte den OH Code werde ich ohne große Lernhürde da auch nicht blicken, hab ja nichtmal den OH1 Code bzgl. des CV Backends voll verinnerlicht --> wohl zu viel Jersey-framework?! oder ich bin zu altmodisch): Sendet das Backend änderungen selbsttätig ohne dass der Client danach pollt? woher weiß das Backend was den Client gerade interessiert? Und wo ist das im Protokoll beschrieben?


            Kommentar


              #7
              Wenn die Verbindung nach senden von Daten nicht unterbrochen wurde, Dann isses auch kein long polling. Benutzt du da was anderes websockets oder so?

              Das Backend kann nichts senden ohne das ein Client pollt, wo soll es die Daten denn hinschicken wenn keine Verbindung zum Client besteht. Der Aufbau der Verbindung erfolgt immer vom Client aus. Der sendet seinen Read Request wobei der URL beliebig viele a Parameter mit den Gruppenadressen angehängt wird. Daher weiß das Backend dann auch für welche Gruppenadressen sich der Client interessiert.
              Gruß
              Tobias

              Kommentar


                #8
                Zitat von peuter Beitrag anzeigen
                Wenn die Verbindung nach senden von Daten nicht unterbrochen wurde, Dann isses auch kein long polling.
                Wieso nicht?

                Benutzt du da was anderes websockets oder so?
                Nope. CV kann afaik kein Websocket.

                Das Backend kann nichts senden ohne das ein Client pollt, wo soll es die Daten denn hinschicken wenn keine Verbindung zum Client besteht.
                Das ist korrekt. Aber wer sagt denn dass man die Verbindung zumachen muss. Es gibt HTTP KeepAlive... da bleibt die Socketverbindung bestehen und man kann mehrfach über eine Verbindung request-response spielen.
                Ich muss aber dazu sagen:
                ich hab dafür nix extra konfiguriert. Ich hab in der CV Einstellung einfach als statt backend="OH" mein eigenes eingetragen, was dann in CV dazu führt, dass einfach die URL anders aussieht. Beispiel backend="kad" --> http://host/kad/ ... gefolgt von der jeweiligen resource (r, l, w, ...)

                Mehr hab ich nicht gemacht. Müsste mal schauen ob im Header vom Client was von keep-alive steht.

                In meinen tests (hab bis dato nur eine einzige CV Seite mit drei Temperaturen drauf) bleibt die Socketverbindung die den read-macht bestehen bis ich im Browser F5 drücke.
                hab ich Client-Seitig mit netstat geprüft: Der Browser-Prozess hält die eine Socketverbindung zum CV-Server aufrecht.

                Der Aufbau der Verbindung erfolgt immer vom Client aus. Der sendet seinen Read Request wobei der URL beliebig viele a Parameter mit den Gruppenadressen angehängt wird. Daher weiß das Backend dann auch für welche Gruppenadressen sich der Client interessiert.
                Soweit war ich schon. Aber wie ist das beim SSE? Der Client macht ein Read... Die Socketverbindung wird aufgebaut, read wird geschickt, Server beantwortet... Und dann? Soweit ich SSe verstanden habe sendet der Server dann selbst (auf der noch bestehenden Socketverbindung), ohne dass der Client nochmal nachfragt Werteänderungen... Wie sieht die Nachricht dafür aus? Ist das eine Abweichung zum Protokoll das man hier nachlesen kann ? --> https://github.com/CometVisu/CometVisu/wiki/Protokoll
                Oder ist das die gleiche Art Nachricht wie direkt nach dem Read-Request, nur eben ohne explizites vorangehendes Read?

                Sehe da aktuell zur persistenten HTTP Verbindung (keep alive) keinen großen Unterschied. Einzig das explizite mehrmalige nachfragen (das erst beantwortet wird wenn es etwas neues gibt) fällt weg....
                Zuletzt geändert von tuxedo; 07.10.2015, 20:46.

                Kommentar


                  #9
                  Gerade nochmal mit Wireshark geschaut:

                  CV sendet im Header jedesmal "Connection: keep-alive" mit. Die Socketverbindung bleibt damit persistent. Und mein HTTP-Server bricht die deshalb auch nicht ab.

                  Was macht SSE denn jetzt tatsächlich so viel besser?

                  [update]
                  Okay, ich hab's verstanden...

                  http://stackoverflow.com/questions/1...and-comet?rq=1

                  Bei SSE geht keine Zeit verloren. Der Client bekommt sofort die Daten geschickt, während beim Longpolling der Server erst etwas von sich gibt wenn er wieder gefragt wird. Und in der zwischenzeit muss der Server Änderungen zwischenspeichern.

                  Jetzt wäre also nur noch zu klären wie das Protokolltechnisch aussieht, wie/ob ich CV dafür konfigurieren muss und wie ich SSE meiner HTTP-Implementierung beibiegen kann.
                  Zuletzt geändert von tuxedo; 07.10.2015, 21:06.

                  Kommentar


                    #10
                    Die einfachste Lösung ist ein Ring-Buffer. Jedes KNX Paket kommt einfach hinten dran. Der Index ist die Position des letzten übertragenen Pakets.

                    Wenn nun während der Read-Verarbeitung am Client neue Pakete am Server eintreffen stört das nicht weiter: der Client fragt nach neuen Paketen mit dem ihm bekannten Index, der Server antwortet sofort mit allen Paketen seit diesem Index und dem nun neuen Index.
                    Sollten keine neuen Pakete eingetroffen sein, so hält der Server die Verbindung so lange offen ohne Antwort bis neue Pakete (typischer Weise natürlich nur eines) eingetroffen sind.

                    Das ganze wiederholt sich nun so lange bis der Client ausgeschaltet wird.

                    Grundsätzlich ist natürlich auch eine Per-Session Lösung mit Client individuellem Index möglich. Das ist aber nicht notwendig und auch kein KISS.
                    TS2, B.IQ, DALI, WireGate für 1wire so wie Server für Logik und als KNX Visu die CometVisu auf HomeCockpit Minor. - Bitte keine PNs, Fragen gehören in das Forum, damit jeder was von den Antworten hat!

                    Kommentar


                      #11
                      Chris, die Index-Geschichte hab ich nun begriffen. Tobias hat aber ServerSentEvents (das die Sache tatsächlich verbessern/vereinfachen würde) ins Spiel gebracht. Hierbei ist mir aber noch unklar wie CV das handhabt.

                      * Muss CV dafür extra eingestellt/konfiguriert werden?
                      * Wie sieht das ganze Protokolltechnisch aus? Wenn der Server ohne extra read-request ein Datenupdate an den Client schicken will: Wie schaut die Nachricht hierfür aus? So wie die Antwort auf einen Read-Request?

                      Kommentar


                        #12
                        Frage 1 kann ich mir zum Teil nun selbst beantworten:

                        aus templateengine.js:

                        Code:
                        ...
                            else if (thisTemplateEngine.backend=="oh2") {
                              // openHAB2 uses SSE and need a new client implementation
                              if(window.EventSource !== undefined){
                                // browser supports EventSource object
                                thisTemplateEngine.visu = new CometVisuOh();
                              } else {
                        ...
                        die SSE Implementierung in CV hängt am oh2 Backend.

                        Ich hab mir das noch nicht im Detail angeschaut. Gut, so viele Backends gibt es für CV nicht und wird es vielleicht auch nicht geben. Aber eine Kommunikationsart an einem bestimmten Backend fest machen ist irgendwie unschön.
                        Damit meine Backendbastelei nun mit SSE funktioniert müsste ich entweder so tun als wäre ich OH2, oder ich müsste CV anpassen :-(

                        [update]
                        Gerade mal in "CometVisuOh" geschaut... Da steckt ja nicht wirlich OH2 spezifisches Zeugs drin. Könnte man auch allgemein formulieren. Dazu bräuchte man aber nicht nur das attribut "backend" das angibt hinter welchem Prefix man in der URL das Backend erreicht, sonder auch ein Attribut die Kommunikationsart (SSE, Comet, ...) einstellt.



                        Fehlt vom Verständnis noch die Sache mit dem Protokoll im SSE Fall.... Da bin ich aus dem CV Code (CometVisuOh) bisher noch nicht so schlau geworden.

                        [update]

                        Scheint wohl so zu sein, dass das Backend im SSE Fall bei neuen Daten einfach die gleiche JSON Nachricht schickt wie im COMET Fall wenn ein Read-Request beantwortet wird. Also was hier:

                        Code:
                        {
                          "d": {
                               "ADDRESS1": VALUE,
                               ...
                               },
                          "i": "INDEX"
                        }
                        Super. Dann muss ich nur noch schauen dass ich meinen HTTP Server dazu bekomme SSE zu können und ggf. einen Pull-Request güt die CV mache wo das mit dem Backend und SSE etwas allgemeingültiger formuliert ist (, aber nicht zu inkompatibilitäten führt).



                        P.S. So langsam fängt's an Spaß zu machen.
                        Zuletzt geändert von tuxedo; 08.10.2015, 07:12.

                        Kommentar


                          #13
                          Sodele. SSE-Funktionalität in den Webserver eingebaut (hat ein wenig gedauert. Wireshark war eine große Hilfe). CV hab ich ein wenig angepasst, so dass für mein Backend wie für OH2 das "CometVisuOh" verwendet wird. Hier hab ich die berücksichtigung des Url-Prefixes über den Konstruktor berücksichtigt statt wie bisher hart codiert.

                          Was soll ich sagen: Läuft ... Jetzt muss mein Backend nur noch die Adressen überwachen und änderungen selbst raus schieben (bis dato wird nur 1x geschoben und nicht überwacht).

                          Super Sache.

                          Gibt's schon Erfahrungswerte ob das mit SSE insgesamt besser/stabiler/zuverlässiger/schneller läuft?

                          Kommentar


                            #14
                            Ja das stand/steht noch auf meiner Todo-Liste, den Client universell konfigurierbar zu machen. Damit man ihn für verschiedene Backends per Parameter konfigurieren. Aber da wir jetzt einen Feature-Freeze haben, warten wir damit erstmal bis das Release raus ist.

                            Langzeit Erfahrungen mit SSE gibts noch nicht, wird je nur mit openHAB2 benutzt und openHAB2 ist selbst noch Alpha, ich habs selbst auch noch nicht produktiv im Einsatz. Also wäre es schön wenn Du da schonmal Erfahrungswerte sammeln könntest.
                            Gruß
                            Tobias

                            Kommentar


                              #15
                              Zitat von peuter Beitrag anzeigen
                              Aber da wir jetzt einen Feature-Freeze haben, warten wir damit erstmal bis das Release raus ist.
                              Alles klar, ich behalte das mal im Auge.

                              Langzeit Erfahrungen mit SSE gibts noch nicht, wird je nur mit openHAB2 benutzt und openHAB2 ist selbst noch Alpha, ich habs selbst auch noch nicht produktiv im Einsatz. Also wäre es schön wenn Du da schonmal Erfahrungswerte sammeln könntest.
                              Werde ich machen.

                              Kommentar

                              Lädt...
                              X